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