如何使用 lens 实现 unsafePartsOf

How to implement unsafePartsOf using lens

我正在尝试使用专用签名实现 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的定义会更容易理解。

但是,此函数不进行类型检查,因为 ^.. 运算符 (toListOf) 具有 toListOf :: Traversal' s a -> s -> [a] 的(专用)签名,其中原始 st(以及 ab)将统一。

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

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

但再次失败,因为使用 modify 需要将 b() 统一。我知道我无法在 getElem 中创建任意 b 所以这个功能是不可能的。

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

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)

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

您可以将 getter 定义为:

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

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