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
。因此,map . first
接收作用于某些元素类型的函数,将其转换为作用于对的函数,然后再将其转换为作用于对列表的函数。
(map . first) :: (a -> b) -> [(a, c)] -> [(b, c)]
< p >
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
和first
的类型。而且透镜也会将作用于结构部分的函数转换为作用于整个结构的函数。在上面的签名中,s
表示整个结构,a
表示其部分。由于我们的输入函数可以将a
的类型更改为b
(如a-> f b
所示),因此我们还需要t
参数,大致表示“在其中将a
更改为b
后s
的类型”。
statusCode
是一个透镜,它将作用于Int
的函数转换为作用于Status
的函数:
statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)
responseStatus
将作用于 Status
的函数转换为作用于 Response
的函数:
responseStatus :: Functor f => (Status -> f Status) -> (Response -> f Response)
< p >
responseStatus.statusCode
的类型遵循与我们在
map.first
中看到的相同模式:
responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response)
目前还不清楚^.
是如何工作的。它与镜头的核心机制和魔法紧密相连; 我不会在这里重申,因为已经有很多关于它的文章了。对于介绍,我建议看看这篇文章和这篇文章,您也可以观看这个优秀的视频。
statusCode
必须是一个光学器,而不是记录字段选择器。我猜他们可能隐藏了字段选择器并导出了同名的光学器;如果你问我,这很令人困惑。(或者编写了自定义Show实例。) - Reid Barton