首页 > 解决方案 > 如何使用镜头实现 unsafePartsOf

问题描述

我正在尝试unsafePartsOf使用专用签名实现一个简单的版本:

unsafePartsOf :: Traversal s t a b -> Lens s t [a] [b]

在尝试了解它是如何工作的之后,我想出了这个(repl)。

myUnsafePartsOf :: Traversal s t a b -> Lens s t [a] [b]
myUnsafePartsOf traverse = lens getter setter
  where
    -- getter :: s -> [a]
    getter s = s ^.. traverse
    -- setter :: s -> [b] -> t
    setter s xs = evalState (s & traverse %%~ assignElem) xs
    assignElem _a = state $ \case
        [] -> error "list not long enough"
        (b : bs) -> (b, bs)

我认为使用lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b分离定义 getter 和 setter 将使它们更容易理解。

但是,此函数不进行类型检查,因为^..operator ( toListOf) 有一个(专门的)签名,toListOf :: Traversal' s a -> s -> [a]说明原始st(以及aand b)将在哪里统一。

然后我尝试使用以下代码实现toListOf相同的 repl )的更通用版本:

toListOf :: Traversal s t a b -> s -> [a]
toListOf traverse s = execState (traverse getElem s) []
  where getElem a = modify (a:)

但再次失败,因为b由于()使用modify. 我知道我无法在中创建任意函数bgetElem所以这个函数是不可能的。

请注意,实际上可以partsOf使用lens类似的方式定义保险箱:

myPartsOf :: Traversal' s a -> Lens' s [a]
myPartsOf t = lens getter setter
  where
    getter s = s ^.. t
    -- setter :: s -> [a] -> s
    setter s xs = evalState (s & t %%~ assignElem) xs
    assignElem a = state $ \case
        [] -> (a, [])
        (x : xs) -> (x, xs)

是否可以定义unsafePartsOf使用lens?如果是这样,我该怎么做?

标签: haskellhaskell-lens

解决方案


您可以定义getter为:

getter s = s ^.. (\f -> Const . getConst . traverse (Const . getConst . f))

它似乎可以进行类型检查。


推荐阅读