haskell - 什么是棱镜?

标签 haskell haskell-lens

我正在尝试更深入地了解 lens图书馆,所以我玩弄它提供的类型。我已经有一些使用镜头的经验,并且知道它们有多么强大和方便。所以我转向棱镜,我有点迷茫。棱镜似乎允许两件事:

  • 确定一个实体是否属于 sum 类型的特定分支,如果是,则捕获元组或单例中的基础数据。
  • 解构和重建实体,可能在过程中对其进行修改。

  • 第一点似乎很有用,但通常不需要实体的所有数据,^?使用普通镜片可以得到 Nothing如果有问题的字段不属于实体所代表的分支,就像它与棱镜一样。

    第二点……不知道,可能有什么用?

    所以问题是:我可以用棱镜做什么,而我不能用其他光学器件?

    编辑 :谢谢大家的优秀答案和进一步阅读的链接!我希望我能全部接受。

    最佳答案

    镜头刻画了has-a关系;棱镜表征 is-a 关系。
    一个 Lens s a说“s 有一个 a”;它有方法可以得到一个 a来自 s并准确覆盖一个 as .一个 Prism s a说“as”;它具有向上转换 a 的方法到 s并(试图)降低 sa .
    将这种直觉放入代码中会为您提供熟悉的“get-set”(或“costate comonad coalgebra”)镜片公式,

    data Lens s a = Lens {
        get :: s -> a,
        set :: a -> s -> s
    }
    
    以及棱镜的“向上向下”表示,
    data Prism s a = Prism {
        up :: a -> s,
        down :: s -> Maybe a
    }
    
    up注入(inject) a进入 s (不添加任何信息)和down测试 s 是否是 a .
    lens , up拼写为 review down preview .没有Prism构造函数;您使用 the prism' smart constructor .

    你可以用 Prism 做什么?注入(inject)和项目总和类型!
    _Left :: Prism (Either a b) a
    _Left = Prism {
        up = Left,
        down = either Just (const Nothing)
    }
    _Right :: Prism (Either a b) b
    _Right = Prism {
        up = Right,
        down = either (const Nothing) Just
    }
    
    镜头不支持这个 - 你不能写 Lens (Either a b) a因为你不能实现get :: Either a b -> a .作为一个实际问题,你可以写一个 Traversal (Either a b) a ,但这不允许您创建 Either a b来自 a - 它只会让你覆盖 a已经在那里了。

    Aside: I think this subtle point about Traversals is the source of your confusion about partial record fields.

    ^? with plain lenses allows getting Nothing if the field in question doesn't belong to the branch the entity represents


    使用 ^?与真正的Lens永远不会返回Nothing , 因为一个 Lens s a准确识别一个 as 内.
    遇到部分记录字段时,
    data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }
    
    makeLenses将生成 Traversal ,而不是 Lens .
    wobble :: Traversal' Wibble Int
    wubble :: Traversal' Wibble Bool
    

    例如Prism s可以在实践中应用,看 Control.Exception.Lens ,它提供了 Prism 的集合s 到 Haskell 的可扩展 Exception等级制度。这使您可以在 SomeException 上执行运行时类型测试。 s 并将特定异常注入(inject) SomeException .
    _ArithException :: Prism' SomeException ArithException
    _AsyncException :: Prism' SomeException AsyncException
    -- etc.
    
    (这些是实际类型的略微简化版本。实际上,这些棱镜是重载的类方法。)
    在更高的层次上思考,某些完整的程序可以被认为是“基本上是一个 Prism”。编码和解码数据就是一个例子:您总是可以将结构化数据转换为 String ,但不是每个 String可以解析回来:
    showRead :: (Show a, Read a) => Prism String a
    showRead = Prism {
        up = show,
        down = listToMaybe . fmap fst . reads
    }
    
    总而言之,Lens es 和 Prism s 一起编码了面向对象编程的两个核心设计工具:组合和子类型。 Lens es 是 Java 的 . 的一流版本。和 =运算符和 Prism s 是 Java 的 instanceof 的一流版本。和隐式向上转型。

    一种富有成效的思考方式Lens es 是它们为您提供了一种拆分组合 s 的方法成为一个专注的值(value)a和一些上下文 c .伪代码:
    type Lens s a = exists c. s <-> (a, c)
    
    在这个框架中,一个 Prism为您提供查看 s 的方法作为 a或某些上下文 c .
    type Prism s a = exists c. s <-> Either a c
    
    (我会让你相信这些与我上面演示的简单表示同构。尝试为这些类型实现 get/set/up/down!)
    在这个意义上,Prism是合作-Lens . Either(,) 的分类对偶; PrismLens 的分类对偶.
    您还可以在 "profunctor optics" 中观察到这种二元性。配方 - Strong Choice 是双重的。
    type Lens  s t a b = forall p. Strong p => p a b -> p s t
    type Prism s t a b = forall p. Choice p => p a b -> p s t
    
    这或多或少是 lens 的表示。使用,因为这些 Lens es 和 Prism s 是非常可组合的。您可以撰写Prism s 变大 Prism s ("as , 这是 p ") 使用 (.) ;组成 PrismLens给你一个Traversal .

    关于haskell - 什么是棱镜?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50915526/

    相关文章:

    haskell - 如何制作 "branched"导管?

    haskell - 如何将 stdout/stderr 通过管道传输到 Turtle 中另一个命令的 stdin?

    haskell - 使用 Data 和 Typeable 获取构造函数的参数类型

    haskell - 如何解决无法使用存在类型的镜头?

    haskell - 组成两个折叠

    amazon-web-services - 使用 Lenses 捕获多个异常

    haskell - 使用镜头访问仿函数内的数据

    Haskell 处理 [IO 字符串]

    c# - F# vs Haskell vs Lisp——学习哪种语言?

    haskell - 变形与镜头有何关系?