4

我一直在阅读这篇文章,并在其中一个部分中指出:

镜头向后构图。我们不能让(.)行为像函数吗?

你是对的,我们可以。我们不是出于各种原因,但直觉是对的。镜头应该像功能一样组合。重要的一件事是 id 可以在不影响任何镜头的情况下预先或后期组合任何镜头。

镜头向后组合是什么意思?

另外,这是什么意思:我们不能让(.)行为像函数吗?

(.)是一个函数,通过将它与 Lens 一起使用,它是否会(.)表现得像其他东西?

4

2 回答 2

6

Lens类型:

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

出于说明目的,我们可以坚持使用不太通用的简单镜头类型,Lens'. 那么右边就变成了:

forall f. Functor f => (a -> f a) -> s -> f s

直观地说,(a -> f a)是对类型为 的结构的一部分的操作s,它被提升为对整个结构的操作,(s -> f s)。(函子类型构造函数f是允许镜头泛化 getter、setter 和许多其他东西的诡计的一部分。我们现在不需要担心它。)换句话说:

  • 从用户的角度来看,镜头允许在给定整体的情况下专注于其中的一部分。
  • 在实现方面,镜头是一种功能,它采用部分功能并导致整体功能。

(请注意,在我刚刚的描述中,“部分”和“整体”是如何以不同的顺序出现的。)

现在,镜头是一个功能,功能可以组合。众所周知,(.)有类型:

(.) :: (y -> z) -> (x -> y) -> (x -> z)

让我们把涉及的类型变成简单的镜头(为了清楚起见,我将放弃约束和forall)。x变成a -> f ay变成s -> f sz变成t -> f t。的特殊类型(.)将是:

((s -> f s) -> t -> f t) -> ((a -> f a) -> s -> f s) -> ((a -> f a) -> t -> f t)

结果我们得到的镜头有 type (a -> f a) -> (t -> f t)。因此,合成镜头firstLens . secondLens对所聚焦的部分进行操作,secondLens并使其成为对所针对的整个结构的操作firstLens。这恰好与组合 OO 样式字段引用的顺序相匹配,这与普通 Haskell 记录访问器的组合顺序相反。

于 2014-04-15T20:36:01.527 回答
4

您可以将Getter镜头的一部分视为一个函数,您可以使用view. 例如写fst函数的lens方式是:

view _1 :: (a,b) -> a

现在观察:

view _1 . view _2 :: (c, (a,b)) -> a  -- First take the second pair element, then the first 
view (_1 . _2)    :: ((b,a) ,c) -> a   -- This is "backwards" (exactly the opposite order of the above)

对于镜头,(.)其行为与功能不同。对于函数,f . g表示“先应用 g,然后应用 f”,但对于镜头,则表示first use the lens f, then use the lens g. 实际上,(.)这两种类型的功能是相同的,但是镜头的类型使它看起来像是倒退了。

于 2014-04-15T19:25:51.577 回答