haskell - 什么是棱镜?
问题描述
我正在尝试更深入地了解lens
库,因此我尝试使用它提供的类型。我已经有一些使用镜头的经验,并且知道它们有多么强大和方便。所以我转向棱镜,我有点迷茫。棱镜似乎允许两件事:
- 确定一个实体是否属于 sum 类型的特定分支,如果是,则捕获元组或单例中的基础数据。
- 解构和重建实体,可能正在修改它。
第一点似乎很有用,但通常不需要来自实体的所有数据,并且^?
使用普通镜头可以获取Nothing
相关字段是否不属于实体所代表的分支,就像使用棱镜一样。
第二点……不知道,可能有什么用?
所以问题是:我可以用棱镜做什么,而我不能用其他光学元件?
编辑:谢谢大家的优秀答案和进一步阅读的链接!我希望我能全部接受。
解决方案
镜头刻画了has-a关系;棱镜表征is-a关系。
ALens s a
说“s
有一个 a
”;它有一些方法可以a
从 an中获取一个s
并覆盖a
一个s
. APrism s a
说“a
是一个 s
”;它具有将an 向上转换a
为 ans
和(尝试)将 an 向下转换s
为 an 的方法a
。
将这种直觉放入代码中会为您提供熟悉的“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
将 ana
注入s
(不添加任何信息),并down
测试是否s
为a
.
在lens
,up
是拼写review
和down
是preview
。没有Prism
构造函数;您使用智能prism'
构造函数。
你可以用 a 做什么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
}
镜头不支持这一点 - 你不能写 aLens (Either a b) a
因为你不能实现get :: Either a b -> a
. 实际上,您可以编写 a Traversal (Either a b) a
,但这不允许您Either a b
从 an创建a
- 它只会让您覆盖a
已经存在的 an 。
旁白:我认为关于
Traversal
s 的这个微妙点是您对部分记录字段感到困惑的根源。
^?
Nothing
如果有问题的字段不属于实体所代表的分支,则使用普通镜头可以获取
^?
与 real 一起使用Lens
将永远不会返回Nothing
,因为 aLens s a
准确地标识了aa
中的一个s
。遇到部分记录字段时,data Wibble = Wobble { _wobble :: Int } | Wubble { _wubble :: Bool }
makeLenses
将生成 aTraversal
,而不是 aLens
。wobble :: Traversal' Wibble Int wubble :: Traversal' Wibble Bool
有关如何Prism
在实践中应用 s 的示例,请查看 to Control.Exception.Lens
,它提供了一组Prism
s 到 Haskell 的可扩展Exception
层次结构中。这使您可以对 s 执行运行时类型测试SomeException
并将特定异常注入SomeException
.
_ArithException :: Prism' SomeException ArithException
_AsyncException :: Prism' SomeException AsyncException
-- etc.
(这些是实际类型的略微简化版本。实际上,这些棱镜是重载的类方法。)
在更高的层次上思考,某些整个程序可以被认为是“基本上是一个Prism
”。编码和解码数据就是一个例子:您总是可以将结构化数据转换为 a 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 是 Javainstanceof
和隐式向上转换的一流版本。
思考Lens
es 的一种卓有成效的方式是,它们为您提供了一种将组合s
拆分为集中值a
和某些上下文的方法c
。伪代码:
type Lens s a = exists c. s <-> (a, c)
在这个框架中, aPrism
为您提供了一种将 as
视为 ana
或 some context的方法c
。
type Prism s a = exists c. s <-> Either a c
(我会让你相信这些与我上面演示的简单表示是同构的。尝试为这些类型实现get
/// ! )set
up
down
在这个意义上 aPrism
是一个co-Lens
。Either
是 的分类对偶(,)
;Prism
是 的范畴对偶Lens
。
您还可以在“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 (" a
is an s
, which is a p
") 用 a(.)
组合a会给你一个.Prism
Lens
Traversal
推荐阅读
- python - Docker 在运行 docker-compose 时以代码 0 退出
- foreign-keys - 在 heidisql 中添加外键时遇到问题
- logging - AEM 自定义错误日志未反映在日志支持下
- javascript - 在 node.js 中创建插入函数(postgresql)
- html - 一些 jpg 文件在上传文件后丢失了旋转信息
- sql-server - “管道的另一端没有进程”随机发生错误
- r - Matlab:绘制 3D 直方图
- php - Can't able to print only search result after scrape search result
- c# - 使用 LINQ GroupBy 链生成具有聚合的多级层次结构
- lstm - RNN 编码器-解码器模型不断过拟合