编辑:我误解了问题,原始答案在最后。
我不知道有什么组合器可以满足你的要求,所以我写了一个。
(^>>=) :: Lens' s a -> (a -> Lens' s b) -> Lens' s b
-- Lens' s a -> (a -> (b -> f b) -> s -> f s) -> (b -> f b) -> s -> f s
-- (That previous line disregards a forall and the Functor constraints)
(x ^>>= f) btofb s = f (s ^. x) btofb s
省略类型签名并询问 ghci 应该给我们最通用的一个,所以这里是:
:t (^>>=)
Getting a s a -> (a -> t1 -> s -> t) -> t1 -> s -> t
Getting 的文档:“当您在类型签名中看到这一点时,它表明您可以向函数传递 Lens、Getter、Traversal、Fold、Prism、Iso 或索引变体之一,它只会“执行正确的事情”。”
右侧同样通用,允许 Traversals/Prisms/etc..
请注意,如果指针不指向自身,这只会产生合法的 lenslikes。
现在应用这个组合器 - 你想要的组合是:
position ^>>= \p -> storage . ix p
这是一个遍历,请参阅原始答案。
或者,使用我喜欢的另一个组合器:
let (f .: g) x = f . g x in position ^>>= (storage .: ix)
任何带有一些中缀声明的你甚至可以去掉那些括号。
(这个原始答案假设position :: Int 局部遮蔽了定位镜头。)
我们不知道列表在那个位置是否有值,所以这不是 Lens',而是 Traversal',代表“遍历任意数量的值”而不是“镜头到一个值上”。
storage . ix position :: Traversal' Example Int
(^?) 将返回遍历的第一个值(如果有),因此如果该位置有效,则该术语将为您提供 Int ,否则为 Nothing。
(^? storage . ix position) :: Example -> Maybe Int
此部分版本将假定该位置有效,否则将崩溃。
(^?! storage . ix position) :: Example -> Int
(%~) 将右侧的函数应用于左侧遍历的所有内容,不仅适用于 Lenses,而且适用于所有 Traversal。 (每个 Lens 都是通过巧妙的 ekmett-trickery 实现的遍历,并且可以插入到 Traversal 可以到达的任何位置。)
storage . ix position %~ (+1) :: Example -> Example
如果您绝对必须使用镜头,如果您尝试在无效位置应用这些部分术语,它们中的任何一个都会崩溃。
singular $ storage . ix position :: Lens' Example Int
storage . singular (ix position) :: Lens' Example Int
PS:您的记录看起来您可能需要拉链:如果您只是渐进式地向前/向后移动,那么如果您跟踪左侧的值列表,那么您会做更少的臭(!!)事情当前位置,您当前位置的值,以及您当前位置右侧的值列表,而不是所有值的列表和您在其中的位置。要获得更多有趣的镜头,请查看Control.Lens.Zipper,但这些都经过优化,可以优雅地嵌套多层拉链。