如果您浏览Lensage上的Lens条目,Lens Github的回购,甚至谷歌关于Lens,您会发现许多部分参考资料,例如介绍性教程/视频,示例,概述等。由于我已经了解了大部分基础知识,因此我正在寻找更完整的参考资料,以帮助我更多地了解高级功能。换句话说,我仍然不知道this意味着什么,并且找不到足够完整的资源来解释这个图形。想法?
答案 0 :(得分:34)
黑线鳕是最好的深度资源。它们包含所有内容,但最初可能有点难以导航。只需浏览不同的模块,并记下哪些地方的心理记录,您很快就会找到自己的方式。您链接的图表也是模块的非常好的地图。
但是,既然您说您不了解图形,我会假设您不想要高级或完整的参考。该图实际上是lens
包的各个部分的基本的高级概述。如果你不了解这个图表,你可能应该等一下高级的东西。
在阅读本文时,请记住,尽管lens
封装最初是作为一个镜头包开始的,但现在包装中有多种光学,它们服从不同的法律,用于不同的事情。 "光学"是用于在数据结构上戳的类似镜头的东西的通用名称。
无论如何,这是我如何阅读图表。
现在,只需查看写入光学元件名称的方框顶部。
顶级Fold
和Setter
是lens
的基本光学元素。可以想象,Setter
是一个设置某个字段值的光学元件。我会在这里省略一些例子,因为你说你知道大部分的基础知识,但基本上,当你做的时候
λ> ellen & age .~ 35
Person { _name = "Ellen", _age = 35 }
然后您将age
用作Setter
。
Getter
是一种特殊的Fold
,它允许您从数据结构中获取值。 (Fold
本身只允许您以某种方式将值组合到一个新值,而不是按原样将它们取出。)Getter
是一种特殊的Fold
标记在箭头指向Getter
到Fold
。
如果你将Fold
和Setter
结合起来(也就是说你结合了循环一堆值的方法和一种设置单个值的方法),你得到一个{{1} }。 Traversal
是一个针对多个元素的光学元素,可让您设置或修改所有元素。
在图表的下方,如果您将Traversal
与Getter
合并,则会获得Traversal
。这应该是您所熟悉的,因为镜头通常被称为“吸气剂和定位器的组合”,您可以将Lens
视为更强大的Traversal
我不会假装我对Setter
,Review
,Prism
和Iso
之间的关系了解很多。我理解它的方式,
Equality
是函数Review
的基本包装,其中b -> t
应该是b
结构中的字段。因此t
获取单个字段值,然后围绕它构造整个结构。这可能看起来很愚蠢,但它实际上与Review
相反,对构建棱镜很有用。Getter
允许您在分支类型中获取和设置值。它是Prism
Either
对元组的意义。您只能通过镜头在Lens
内获取值,因为如果它到达Either
分支,它就会爆炸。Left
是Iso
和Lens
的强大组合,可让您自由地查看"两种方式"通过光学。虽然Prism
允许您从高级别查看到数据结构的精确部分,但Lens
还允许您从数据结构的精确部分查看到高级别。Iso
是......我不知道,抱歉。类似Equality
的类型签名一开始可能会让人感到恐惧,所以我会尝试快速涵盖它的含义。如果您查看Lens s t a b
,您会看到其类型签名是
Getter
如果我们考虑概念上的吸气剂,这似乎很熟悉。什么是吸气剂?它是从数据结构Getter s a
到该结构内部的单个值s
的函数。例如,函数a
是一个从Person -> Age
对象获取年龄的getter。相应的Person
只有签名
Getter
真的那么简单。 Getter Person Age
是一种可以从Getter s a
内获取a
的视觉效果。
简化的s
签名现在应该不那么可怕了。如果你不得不猜测,那是什么
Lens'
?简单!它是Lens' Person Age
可以获取并设置(还记得镜头是getter和setter的组合吗?)Lens
字段中的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"
从字符串中生成ID号怎么办?你可能想这样做:
toIdNumber :: String -> Int
但是你可以λ> carolyn & idField %~ toIdNumber
Person 57123 "North Street 12"
,因为那个镜头只能处理idField :: Lens' (Person String) String
s。它不知道将字符串转换为整数并将其粘贴到同一位置意味着什么。这就是我们String
。
我读取签名的方式是#34;如果你给我一个函数Lens s t a b
,我会给你一个函数a -> b
",其中s -> t
和{ {1}}都指向镜头的目标,a
和b
指的是包含数据结构。具体例子:如果我们有
s
我们可以进行上面的转换。该镜头知道如何将带有字符串ID字段的t
转换为具有idField :: Lens (Person String) (Person Int) String Int
id字段的人。
因此,基本上,Person
可以被理解为"函数" Int
。 "给我一个转换Lens s t a b
来做我的目标,以及一个包含该目标的数据结构(a -> b) -> (s -> t)
,我将返回一个数据结构(a -> b)
,在那里进行转换已被应用。"
当然,它实际上并不是那个功能 - 它比那更强大 - 但是你可以把它变成s
,这是{{1}的一部分。 }}
盒子的内容只是每种光学元件的最常见和/或核心操作。他们的类型很多地说明了他们做了什么,我会选择一些例子来说明我的意思。
通常,最重要的项目是你如何构建这种光学元件。例如,t
框中最顶部的项是over
函数,它可以从任何函数Setter
构造Getter
。如果您使用to
功能,则Getter
免费获得s -> a
。
然后在下面是常见的操作。您可以在Traversal
下找到Traversable
,这就是您使用traverse
从数据结构中获取内容的方式。在view
中,您会发现Getter
和Getter
高位。
(有趣的观察:大多数盒子似乎包含两个双重功能:一种创建光学器件的方法,以及一种使用它们的方法。有趣的是,那些通常具有几乎相同的类型签名,但翻转比较彼此。示例:
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
相似。)当我确定了这一点时,我会潜入Haddocks以获取这些模块(在这个例子,data Gendered a = Masculine a | Feminine a
),搜索我需要的操作。
我学习光学以及如何使用它们的方式与学习一切相同。我发现一个真正的问题,光学是一个很好的解决方案,我试图解决它。我尝试了但是我失败了,我再试一次,我寻求帮助,我尝试尝试尝试。最终我会成功。然后我尝试了一些略有不同的东西。
通过实际使用它们的方式,我会收集很多关于它们如何工作的有用经验。
在我开始撰写本文之前,我认为您需要_Right
来处理_Feminine
值,因为Prism
类型是分支,不是吗?不完全的!至少不超过Prism
类型,您不需要Maybe
来处理。
因为Maybe
类型是零个或一个元素的容器,所以实际上只需要一个List
来处理它。当你有两个分支可以包含不同的值时,你需要Prism
的全部功能。 (要构造 Maybe
值,您仍然需要Traversal
。)
只有当我开始非常仔细地阅读图表并探索模块以找出光学系统之间的实际,形式差异时,我才想到这一点。这是使用我的方法来学习东西的缺点。只要它有效,我就是它。有时会导致迂回的做事方式。
答案 1 :(得分:1)
图形是两种类型之间弱关系所能做的事情,以及强关系所能做的事情。
在最弱的情况下,您可以在a
类型中折叠s
类型的“元素”,或者可以在a
内将b
设置为s
{1}},将其更改为t
(a
和b
当然可以与s
和t
相同。在最底层,你有平等;一步,你有同构;镜头和棱镜等
从另一个角度来看,由于关系的要求,它从最适用的流向最不适用:有许多类型可以在概念上“折叠”在某些a
之内。然而,与a
相比,更少的东西将是平等的或同构的。