18

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?

4

2 回答 2

34

黑线鳕是最好的深度资源。它们包含所有内容,但起初可能有点难以导航。只需浏览不同的模块并记下在哪里,您很快就会开始找到自己的方式。您链接到的图表也是一个非常好的模块图。

但是,由于您说您不了解图形,我将假设您不想要高级或完整的参考。该图实际上是包的各个部分的非常基本的高级概述lens。如果您不理解图表,您可能应该等待一些高级的东西。

阅读本文时,请记住,尽管该lens包装最初是一个镜片包装,但现在包装中包含多种光学器件,它们遵循不同的法律并用于不同的用途。“光学”是你用来戳数据结构的类似镜头的东西的总称。

无论如何,这就是我阅读图表的方式。

盒子的安排

现在,只需查看写有光学元件名称的框的顶部即可。

  1. 顶层FoldSetter是 的基本光学lens。可以想象,aSetter是一种设置某些字段值的光学器件。我将在这里省略一堆示例,因为您说您知道大部分基础知识,但本质上,当您这样做时

    λ> ellen & age .~ 35
    Person { _name = "Ellen", _age = 35 }
    

    那么你已经age用作Setter.

  2. AGetter是一种特殊的类型,Fold它允许您从数据结构中获取值。(AFold本身只允许您以某种方式将值组合成一个新值,而不是按原样取出它们。) aGetter是一种特殊的Fold,在图形中由指向 from 的箭头标记GetterFold

  3. 如果将 aFold和 a结合起来Setter(即结合了一种循环一堆值的方法和一种设置单个值的方法),你会得到一个Traversal. ATraversal是一种针对多个元素的光学元件,可让您设置或修改所有元素。

  4. 在图表的下方,如果将 aGetter与 a结合起来,Traversal则会得到 a Lens。这对您来说应该很熟悉,因为镜头经常被称为“getter 和 setter 的组合”,您可以将 aTraversal视为更强大的Setter.

  5. 我不会假装我对 , 和 之间的Review关系Prism了解Iso很多Equality。按照我的理解,

    • AReview是一个函数的基本包装器b -> tb它应该是t结构内的一个字段。所以 aReview接受一个字段值,然后围绕它构建一个完整的结构。这可能看起来很愚蠢,但它实际上是 a 的反面Getter,并且对构建棱镜很有用。
    • APrism允许您在分支类型中获取和设置值。这Either就是 aLens与元组的关系。你不能Either只用一个镜头在 an 内获得一个值,因为如果它碰到Left分支就会爆炸。
    • AnIso是 和 的一个非常强大的组合LensPrism它可以让您通过光学“双向”自由地观察。虽然 aLens可以让您从高级别查看数据结构的精确部分,但Iso您还可以从数据结构的精确部分查看高级别。
    • 一个Equality是......我不知道,对不起。

插曲:类型签名

像这样的类型签名一Lens s t a b开始可能会很吓人,所以我将尝试快速介绍它的含义。如果你看一下Getter,你会看到它的类型签名是

Getter s a

如果我们从概念上考虑什么是吸气剂,这可能看起来很熟悉。什么是吸气剂?它是从数据结构s到该结构内的单个值的函数a。例如,函数是从对象Person -> Age获取年龄的 getter 。Person对应的Getter只是有签名

Getter Person Age

真的就是这么简单。AGetter s a是一种可以as.

简化的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因为那个镜头只能处理Strings。它不知道将字符串转换为整数并将其粘贴到同一位置意味着什么。这就是为什么我们有Lens s t a b.

我读那个签名的方式是“如果你给我一个函数a -> b,我会给你一个函数s -> t”,其中ab都指的是镜头的目标,s并且t指的是包含的数据结构。具体例子:如果我们有

idField :: Lens (Person String) (Person Int) String Int

我们可以做上面的转换。该镜头知道如何将Person带有字符串 id 字段的 a 转换为带有Intid 字段的人。

因此,从本质上讲, aLens s t a b可以读作“函数” (a -> b) -> (s -> t)。“给我一个(a -> b)要对我的目标进行的转换,以及一个s包含该目标的数据结构,我将返回一个t应用了该转换的数据结构。”

当然,它实际上并不是那个函数——它比那个更强大——但是你可以通过将它赋予overwhich is part of来将它变成那个函数Setter

盒子的内容

方框中的内容只是每种光学器件最常见和/或核心的操作。他们的类型对他们所做的事情说了很多,我将选择几个例子来说明我的意思。

通常,最重要的项目是您如何构建这种光学器件。例如,Getter框中最上面的项目是函数,它从任何函数to构造 a 。如果您使用该功能,您可以从 a 免费获得 a 。Getters -> aTraversalTraversabletraverse

然后下面是常见的操作。例如,您将在view下找到Getter,这是您使用 aGetter从数据结构中获取某些内容的方式。在Setter中,您发现overset高高在上。

(有趣的观察:大多数盒子似乎包含两个双重功能:一种创建光学元件的方法,以及一种使用它们的方法。有趣的是,它们通常具有几乎相同的类型签名,但相互比较时会翻转。例如:

  • 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)

只是我现在注意到的一些有趣的事情。)

我如何使用图形

我通常不会像写这篇文章那样仔细研究图形。大多数情况下,我只是偷看SetterGetterTraversal和它们之间的关系,因为这些是我最常使用的光学元件LensPrism

当我知道我需要做某事时,我通常会快速浏览一下图形,看看哪些框包含与我想做的类似的操作。(例如,如果我有data Gendered a = Masculine a | Feminine a,则_Right类似于假设的_Feminine。)当我确定这一点时,我会深入研究那些模块的 Haddocks(在本例中Prism为 ),搜索我需要的操作。

我如何了解光学

我学习光学以及如何使用它们,就像我学习所有东西一样。我发现了一个真正的问题,光学是一个很好的解决方案,我尝试解决它。我尝试过,但失败了,我再试一次,我寻求帮助,我尝试,我尝试,我尝试。最终我会成功。然后我尝试一些稍微不同的东西。

通过实际使用事物的方式,我收集了很多关于它们如何工作等的有用经验。

奖金:个人误解

在我开始写这篇文章之前,我认为你需要Prisms 来处理Maybe值,因为Maybe类型是分支的,不是吗?不完全的!至少不超过List你不需要Prisms 处理的类型。

因为Maybe类型是一个包含零个或一个元素的容器,所以你实际上只需要 aTraversal来处理它。当您有两个可以包含不同值的分支时,您开始需要Prism. (要构造一个Maybe值,您仍然需要Review它。)

只有当我开始非常仔细地阅读图表并探索模块以找出光学器件之间实际的形式差异时,我才明白这一点。这就是用我的方法学东西的缺点。只要它有效,我只是翼它。有时这会导致迂回的做事方式。

于 2015-04-20T08:22:44.467 回答
1

图形是从你可以用两种类型之间的弱关系做什么到你可以用强关系做什么的流程。

在最弱的情况下,您可以在 type 中折叠 type 的“元素” as或者您可以在 an中设置aa ,将其更改为 a (当然,并且可以与and相同)。在最底层,你们是平等的;上一步,你有同构;然后是透镜和棱镜等。bstabst

从另一个角度来看,由于关系的要求,它从最适用向下流向最不适用:有许多类型可以按照a概念上“在”它们中的一些来折叠;而更少的东西会等于或同构于a.

于 2016-01-21T21:51:51.210 回答