If you browse through Lens entry on hackage, Lens Github's repo, or even google about Lens, you will find a lot of partial references such as introductory tutorials/videos, examples, overviews and so on. Since I already know most of the basics, I am looking for a more complete reference that would help me to get more knowledge about the advanced features. In other words, I still have no idea what this means and couldn't find a resource complete enough to explain this graphic as a whole. Ideas?
2 回答
黑线鳕是最好的深度资源。它们包含所有内容,但起初可能有点难以导航。只需浏览不同的模块并记下在哪里,您很快就会开始找到自己的方式。您链接到的图表也是一个非常好的模块图。
但是,由于您说您不了解图形,我将假设您不想要高级或完整的参考。该图实际上是包的各个部分的非常基本的高级概述lens
。如果您不理解图表,您可能应该等待一些高级的东西。
阅读本文时,请记住,尽管该lens
包装最初是一个镜片包装,但现在包装中包含多种光学器件,它们遵循不同的法律并用于不同的用途。“光学”是你用来戳数据结构的类似镜头的东西的总称。
无论如何,这就是我阅读图表的方式。
盒子的安排
现在,只需查看写有光学元件名称的框的顶部即可。
顶层
Fold
和Setter
是 的基本光学lens
。可以想象,aSetter
是一种设置某些字段值的光学器件。我将在这里省略一堆示例,因为您说您知道大部分基础知识,但本质上,当您这样做时λ> ellen & age .~ 35 Person { _name = "Ellen", _age = 35 }
那么你已经
age
用作Setter
.A
Getter
是一种特殊的类型,Fold
它允许您从数据结构中获取值。(AFold
本身只允许您以某种方式将值组合成一个新值,而不是按原样取出它们。) aGetter
是一种特殊的Fold
,在图形中由指向 from 的箭头标记Getter
到Fold
。如果将 a
Fold
和 a结合起来Setter
(即结合了一种循环一堆值的方法和一种设置单个值的方法),你会得到一个Traversal
. ATraversal
是一种针对多个元素的光学元件,可让您设置或修改所有元素。在图表的下方,如果将 a
Getter
与 a结合起来,Traversal
则会得到 aLens
。这对您来说应该很熟悉,因为镜头经常被称为“getter 和 setter 的组合”,您可以将 aTraversal
视为更强大的Setter
.我不会假装我对 , 和 之间的
Review
关系Prism
了解Iso
很多Equality
。按照我的理解,- A
Review
是一个函数的基本包装器b -> t
,b
它应该是t
结构内的一个字段。所以 aReview
接受一个字段值,然后围绕它构建一个完整的结构。这可能看起来很愚蠢,但它实际上是 a 的反面Getter
,并且对构建棱镜很有用。 - A
Prism
允许您在分支类型中获取和设置值。这Either
就是 aLens
与元组的关系。你不能Either
只用一个镜头在 an 内获得一个值,因为如果它碰到Left
分支就会爆炸。 - An
Iso
是 和 的一个非常强大的组合Lens
,Prism
它可以让您通过光学“双向”自由地观察。虽然 aLens
可以让您从高级别查看数据结构的精确部分,但Iso
您还可以从数据结构的精确部分查看高级别。 - 一个
Equality
是......我不知道,对不起。
- A
插曲:类型签名
像这样的类型签名一Lens s t a b
开始可能会很吓人,所以我将尝试快速介绍它的含义。如果你看一下Getter
,你会看到它的类型签名是
Getter s a
如果我们从概念上考虑什么是吸气剂,这可能看起来很熟悉。什么是吸气剂?它是从数据结构s
到该结构内的单个值的函数a
。例如,函数是从对象Person -> Age
获取年龄的 getter 。Person
对应的Getter
只是有签名
Getter Person Age
真的就是这么简单。AGetter s a
是一种可以a
从s
.
简化的Lens'
签名现在应该不那么可怕了。如果你不得不猜测,什么是
Lens' Person Age
? 简单的!它是一个Lens
可以获取和设置(还记得镜头是 getter 和 setter 的组合吗?)值Age
内的字段Person
。因此,您应用于Getter s a
您的相同逻辑可以应用于Lens' s a
.
但是那s t a b
部分呢?好吧,想想这种类型:
data Person a = { _idField :: a, address :: String }
一个人可以通过一些识别值来识别并且有一个年龄。假设你被一个名字识别
carolyn :: Person String
carolyn = Person "Carolyn" "North Street 12"
如果你有一个toIdNumber :: String -> Int
从字符串中生成 ID 号的函数怎么办?您可能想要这样做:
λ> carolyn & idField %~ toIdNumber
Person 57123 "North Street 12"
但你不能,idField :: Lens' (Person String) String
因为那个镜头只能处理String
s。它不知道将字符串转换为整数并将其粘贴到同一位置意味着什么。这就是为什么我们有Lens s t a b
.
我读那个签名的方式是“如果你给我一个函数a -> b
,我会给你一个函数s -> t
”,其中a
和b
都指的是镜头的目标,s
并且t
指的是包含的数据结构。具体例子:如果我们有
idField :: Lens (Person String) (Person Int) String Int
我们可以做上面的转换。该镜头知道如何将Person
带有字符串 id 字段的 a 转换为带有Int
id 字段的人。
因此,从本质上讲, aLens s t a b
可以读作“函数” (a -> b) -> (s -> t)
。“给我一个(a -> b)
要对我的目标进行的转换,以及一个s
包含该目标的数据结构,我将返回一个t
应用了该转换的数据结构。”
当然,它实际上并不是那个函数——它比那个更强大——但是你可以通过将它赋予over
which is part of来将它变成那个函数Setter
。
盒子的内容
方框中的内容只是每种光学器件最常见和/或核心的操作。他们的类型对他们所做的事情说了很多,我将选择几个例子来说明我的意思。
通常,最重要的项目是您如何构建这种光学器件。例如,Getter
框中最上面的项目是函数,它从任何函数to
构造 a 。如果您使用该功能,您可以从 a 免费获得 a 。Getter
s -> a
Traversal
Traversable
traverse
然后下面是常见的操作。例如,您将在view
下找到Getter
,这是您使用 aGetter
从数据结构中获取某些内容的方式。在Setter
中,您发现over
并set
高高在上。
(有趣的观察:大多数盒子似乎包含两个双重功能:一种创建光学元件的方法,以及一种使用它们的方法。有趣的是,它们通常具有几乎相同的类型签名,但相互比较时会翻转。例如:
to :: (s -> a) -> Getter s a
view :: Getter s a -> (s -> a)
和
unto :: (b -> t) -> Review s t a b
review :: Review s t a b -> (b -> t)
只是我现在注意到的一些有趣的事情。)
我如何使用图形
我通常不会像写这篇文章那样仔细研究图形。大多数情况下,我只是偷看Setter
、Getter
、Traversal
和它们之间的关系,因为这些是我最常使用的光学元件Lens
。Prism
当我知道我需要做某事时,我通常会快速浏览一下图形,看看哪些框包含与我想做的类似的操作。(例如,如果我有data Gendered a = Masculine a | Feminine a
,则_Right
类似于假设的_Feminine
。)当我确定这一点时,我会深入研究那些模块的 Haddocks(在本例中Prism
为 ),搜索我需要的操作。
我如何了解光学
我学习光学以及如何使用它们,就像我学习所有东西一样。我发现了一个真正的问题,光学是一个很好的解决方案,我尝试解决它。我尝试过,但失败了,我再试一次,我寻求帮助,我尝试,我尝试,我尝试。最终我会成功。然后我尝试一些稍微不同的东西。
通过实际使用事物的方式,我收集了很多关于它们如何工作等的有用经验。
奖金:个人误解
在我开始写这篇文章之前,我认为你需要Prism
s 来处理Maybe
值,因为Maybe
类型是分支的,不是吗?不完全的!至少不超过List
你不需要Prism
s 处理的类型。
因为Maybe
类型是一个包含零个或一个元素的容器,所以你实际上只需要 aTraversal
来处理它。当您有两个可以包含不同值的分支时,您开始需要Prism
. (要构造一个Maybe
值,您仍然需要Review
它。)
只有当我开始非常仔细地阅读图表并探索模块以找出光学器件之间实际的形式差异时,我才明白这一点。这就是用我的方法学东西的缺点。只要它有效,我只是翼它。有时这会导致迂回的做事方式。
图形是从你可以用两种类型之间的弱关系做什么到你可以用强关系做什么的流程。
在最弱的情况下,您可以在 type 中折叠 type 的“元素” a
,s
或者您可以在 an中设置a
a ,将其更改为 a (当然,并且可以与and相同)。在最底层,你们是平等的;上一步,你有同构;然后是透镜和棱镜等。b
s
t
a
b
s
t
从另一个角度来看,由于关系的要求,它从最适用向下流向最不适用:有许多类型可以按照a
概念上“在”它们中的一些来折叠;而更少的东西会等于或同构于a
.