的正确读法r ^. responseStatus . statusCode
是r ^. (responseStatus . statusCode)
。这是很自然的,因为函数组合在应用于两个参数时返回一个函数,因此(r ^. responseStatus) . statusCode
必须返回一个函数,而不是任何可以打印出来的值。
这仍然留下了为什么镜头以“错误”顺序组成的问题。由于镜头的实现有点神奇,我们来看一个更简单的例子。
first
是一个映射到对的第一个元素的函数:
first :: (a -> b) -> (a, c) -> (b, c)
first f (a, b) = (f a, b)
做什么map . first
?first
接受一个作用于第一个元素的函数,并返回一个作用于一对的函数,如果我们以这种方式将类型括起来,则更明显:
first :: (a -> b) -> ((a, c) -> (b, c))
另外,回忆一下 的类型map
:
map :: (a -> b) -> ([a] -> [b])
map
接受作用于元素的函数并返回作用于列表的函数。现在,f . g
首先应用g
然后将结果提供给f
. Somap . first
获取一个作用于某种元素类型的函数,将其转换为作用于对的函数,然后将其转换为作用于对列表的函数。
(map . first) :: (a -> b) -> [(a, c)] -> [(b, c)]
first
并且map
两者都将作用于结构的一部分的功能转变为作用于整个结构的功能。map . first
其中,整体结构是为了什么成为first
焦点map
。
(map . first) (+10) [(0, 2), (3, 4)] == [(10, 2), (13, 4)]
现在来看看镜头的类型:
type Lens = forall f. Functor f => (a -> f b) -> (s -> f t)
现在尝试忽略这些Functor
位。如果我们稍微眯起眼睛,这类似于map
and的类型first
。发生这种情况时,镜头也会将作用于部分结构的功能转换为作用于整个结构的功能。在上面的签名s
中表示整个结构并a
表示它的一部分。由于我们的输入函数可以改变a
to的类型b
(用 表示a -> f b
),所以我们还需要t
参数,大致意思是“s
我们改成a
里面的类型b
”。
statusCode
Int
是将作用于 a 的函数转换为作用于a的函数的透镜Status
:
statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)
responseStatus
将作用于 aStatus
的函数转换为作用于 a 的函数Response
:
responseStatus :: Functor f => (Status -> f Status) -> (Response -> f Response)
的类型responseStatus . statusCode
遵循与我们看到的相同的模式map . first
:
responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response)
究竟是如何工作的还有待观察^.
。它与镜头的核心机制和魔力密切相关;我不会在这里重复它,因为有很多关于它的著作。对于介绍,我建议您查看这个和这个,您也可以观看这个出色的视频。