【问题标题】:Haskell use first level lenses to create complex lensHaskell 使用一级镜头创建复杂镜头
【发布时间】:2016-04-12 10:04:58
【问题描述】:

假设我有一个包含两个字段的对象:

data Example = Example { _position :: Int
                       , _storage  :: [Int]}

如何构建一个聚焦position 元素的镜头,该元素位于storage 内?

另外,是否可以将通过镜头修改的 position 值限制在基于 storage 大小的范围内?

似乎alongside 可以以某种方式使用,因为Example 与元组同构,但我无法理解这样做的方法。

我不知道如何表达这个问题,所以我找不到很多相关信息。

【问题讨论】:

  • 您的用例是什么?模索引边界错误Example 看起来像是Store comonad 的变体。我想知道那台机器是否更适合您的需求
  • 我的实际用例不需要边界检查,但确实需要使用存储在对象中的一些数据来构造镜头。但是由于克制也是一个有趣的案例,我会看看Store,谢谢。

标签: haskell haskell-lens


【解决方案1】:

编辑:我误解了问题,原始答案在最后。

我不知道有什么组合器可以满足你的要求,所以我写了一个。

(^>>=) :: 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,但这些都经过优化,可以优雅地嵌套多层拉链。

【讨论】:

  • 当我写storage . ix position我得到一个类型错误,即Couldn't match type 'Int' with '(Int -> f0 Int) -> Example -> f0 Example' Expected type: Control.Lens.At.Index [Int] Actual type: (Int -> f0 Int) -> Example -> f0 Example In the first argument of 'ix', namely 'position' In the second argument of '(.)', namely 'ix position'
  • 射击,我以为你用 Int 类型的局部变量遮蔽了位置,该变量已经指向列表中的正确位置。我现在明白了这个问题。嗯。
  • 我发现这个stackoverflow.com/questions/21255541/… 的问题可以解决同样的问题(虽然有一点不同),现在我正在尝试用lens get set 创建镜头,但还没有成功。
  • 哇,^>>= 对我来说太神秘了,但这似乎是我真正想到的——就像一个插入对象的附加套接字。我会尝试解决这个问题,谢谢。
【解决方案2】:

看来,实现这一点最简单的方法是使用镜头编写getter和setter,然后组成一个镜头:

at_position :: Functor f => (Int -> f Int) -> Example -> f Example
at_position = lens get set
    where
        get :: Example -> Int
        get e = fromJust $ e ^? storage . ix (e^.position)

        set :: Example -> Int -> Example
        set e v = e & storage . ix (e^.position) .~ v

虽然这可能会有所改进,但代码足够清晰,并且不限于对象结构。

【讨论】:

    【解决方案3】:

    我认为棱镜应该更适合这种情况,因为pos 可以是不大于您的列表长度的整数,也可以是负数。

    我认为您可以使用 docu for prisms 提供之类的东西

    nat :: Prism' Integer Natural
    nat = prism toInteger $ \ i ->
       if i < 0
       then Left i
       else Right (fromInteger i)
    
    storageAtPos Prism' Example Int
    storageAtPos = prism $ aux
      where aux (Example p s) | p < 0 || p >= length s = Nothing
                              | otherwise = Just (s !! p)
    

    注意:我没有运行此代码 - 只是对文档做了一个模拟(现在没有 ghc)

    更新

    也许像

    <s>storageAtPos = \p -&gt; (p^.storage)^?(ix $ p^.pos)</s>

    有效,但同样 - 我现在没有要测试的 ghc - 正如@Gurkenglas 指出的那样,这不是Prism

    【讨论】:

    • 重点是创建setter/getter,而不需要窥视我将聚焦镜头的对象。棱镜将允许修改/查看值,但模式匹配Example p s 真的不可避免,还是仅使用镜头就可以实现?这是手头的主要问题:通过组合镜头来创建与精确实现没有紧密联系的镜头(或棱镜)。
    • 棱镜是遍历最多一个值的遍历,如果它们确实遍历一个值,则遍历所有可用信息。我们既不会遍历记录的位置部分,也不会遍历存储的其余部分,因此这不是 Prism。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 2017-05-06
    • 2022-01-04
    • 2021-08-09
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多