Haskell 什么是棱镜?

Haskell 什么是棱镜?,haskell,haskell-lens,Haskell,Haskell Lens,我试图对lenslibrary有更深入的理解,所以我尝试使用它提供的类型。我已经有了一些镜片的经验,知道它们有多强大和方便。所以我转到棱镜,我有点迷路了。棱镜似乎允许两件事: 确定实体是否属于sum类型的特定分支,如果属于,则捕获元组或单例中的底层数据 分解和重构实体,可能在过程中对其进行修改 第一点似乎很有用,但通常不需要实体中的所有数据,而带有普通透镜的^?允许在所讨论的字段不属于实体表示的分支的情况下获取任何内容,就像使用棱镜一样 第二点。。。我不知道,可能有用吗 所以问题是:我能用棱镜做

我试图对
lens
library有更深入的理解,所以我尝试使用它提供的类型。我已经有了一些镜片的经验,知道它们有多强大和方便。所以我转到棱镜,我有点迷路了。棱镜似乎允许两件事:

  • 确定实体是否属于sum类型的特定分支,如果属于,则捕获元组或单例中的底层数据
  • 分解和重构实体,可能在过程中对其进行修改
  • 第一点似乎很有用,但通常不需要实体中的所有数据,而带有普通透镜的
    ^?
    允许在所讨论的字段不属于实体表示的分支的情况下获取任何内容,就像使用棱镜一样

    第二点。。。我不知道,可能有用吗

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


    编辑:感谢大家提供了精彩的答案和链接,以便进一步阅读!我希望我能全部接受。

    镜头是has-a关系的特征;棱镜是is-a关系的特征。

    一个
    镜头sa
    说“
    s
    有一个
    A
    ”;它具有从
    s
    中准确获取一个
    a
    并在
    s
    中准确覆盖一个
    a
    的方法。一个
    s棱镜A
    表示“
    A
    是一个
    s
    ”;它具有将
    a
    向上转换为
    s
    和(尝试)将
    s
    向下转换为
    a
    的方法

    将这种直觉转化为代码会让你熟悉镜头的“设置”(或“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
    a
    注入
    s
    (不添加任何信息),而
    down
    测试
    s
    是否为
    a

    lens
    中,拼写为
    up
    ,拼写为
    down
    。没有
    Prism
    构造函数;你用


    使用
    棱镜可以做什么?注入和项目总和类型

    _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
    }
    
    镜头不支持此功能-您无法编写
    镜头(或a b)a
    ,因为您无法实现
    get::或a b->a
    。实际上,您可以编写一个
    遍历(a b)a
    ,但这不允许您从
    a
    创建
    a b
    ——它只允许您覆盖已经存在的
    a

    旁白:我认为关于
    遍历
    s的这一微妙之处是您对部分记录字段感到困惑的根源

    ^?
    使用普通镜头,如果所讨论的字段不属于实体表示的分支,则允许获取任何内容

    用真实的
    镜头使用
    ^?
    将永远不会返回
    什么
    ,因为
    镜头sa
    镜头s
    中只识别一个
    a
    。 当遇到部分记录字段时

    data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }
    
    makelens
    将生成
    遍历
    ,而不是
    镜头

    wobble :: Traversal' Wibble Int
    wubble :: Traversal' Wibble Bool
    
    有关如何在实践中应用
    Prism
    s的示例,请参阅,它将
    Prism
    s的集合提供给Haskell的可扩展
    Exception
    层次结构。这允许您对
    SomeException
    s执行运行时类型测试,并将特定异常注入
    SomeException

    _ArithException :: Prism' SomeException ArithException
    _AsyncException :: Prism' SomeException AsyncException
    -- etc.
    
    (这些是实际类型的稍微简化版本。实际上,这些棱柱体是重载类方法。)

    从更高的层次来看,某些完整的程序可以被认为是“基本上是一个
    棱镜”。编码和解码数据就是一个例子:您始终可以将结构化数据转换为
    字符串
    ,但并非每个
    字符串
    都可以解析回:

    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
    分解为聚焦值
    a
    和一些上下文
    c
    的方法。伪代码:

    type Lens s a = exists c. s <-> (a, c)
    
    (我将让您自己确信这些与我上面演示的简单表示是同构的。请尝试为这些类型实现
    get
    /
    set
    /
    up
    /
    down

    从这个意义上说,
    棱镜
    是一个共透镜
    或者是
    (,)
    的范畴对偶
    Prism
    Lens
    的范畴对偶

    wobble :: Traversal' Wibble Int
    wubble :: Traversal' Wibble Bool
    
    你也可以在公式中观察到这种二元性——它们是二元的

    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(
    a
    s
    ,它是
    p
    );用
    Lens
    组合
    Prism
    可以让你进行
    遍历
    我刚刚写了一篇博客文章,这可能有助于建立关于Prism的一些直觉:Prism是构造器(Lens是字段)


    棱镜可以作为一级模式匹配引入,但这是一个复杂的问题 片面的观点。我会说他们是泛化的构造函数,尽管可能是这样 通常用于模式匹配而不是实际构造

    构造器(和合法棱镜)的重要属性是其 注入能力。T
    preview l (review l b) ≡ Just b
    
    preview l s ≡ Just a ⇒ review l a ≡ s
    
    review l x ≡ review l y ⇒ x ≡ y
    
    review l x ≡ review l y
      -- x ≡ y -> f x ≡ f y
    preview l (review l x) ≡ preview l (review l y)
      -- rewrite both sides with the first law
    Just x ≡ Just y
      -- injectivity of Just
    x ≡ y
    
    -- Bad!
    _CI :: FoldCase s => Prism' (CI s) s
    _CI = prism' ci (Just . foldedCase)
    
    λ> review _CI "FOO" == review _CI "foo"
    True
    
    λ> "FOO" == "foo"
    False
    
    λ> preview _CI (review _CI "FOO")
    Just "foo"
    
     Traversal
        / \
       /   \
      /     \
    Lens   Prism
      \     /
       \   /
        \ /
        Iso