[英]How Haskell's “composing lenses using function composition” with that weird order of arguments could be implemented?
我一直在阅读一个wreq教程 :
镜头提供了一种聚焦于Haskell值的一部分的方法。 例如,
Response
类型有一个responseStatus
镜头,它关注服务器返回的状态信息。ghci> r ^. responseStatus Status {statusCode = 200, statusMessage = "OK"}
^.
operator将一个值作为其第一个参数,将镜头作为其第二个参数,并返回镜头聚焦的值部分。我们使用功能组合构成镜头,这使我们可以轻松地专注于深层嵌套结构的一部分。
ghci> r ^. responseStatus . statusCode 200
我无法想出如何使用这个参数顺序完成的函数组合可以按顺序处理嵌套结构。
看: r ^. responseStatus . statusCode
r ^. responseStatus . statusCode
r ^. responseStatus . statusCode
可以是r ^. (responseStatus . statusCode)
r ^. (responseStatus . statusCode)
或(r ^. responseStatus) . statusCode
r ^. (responseStatus . statusCode)
(r ^. responseStatus) . statusCode
(r ^. responseStatus) . statusCode
。
在第一个说我们构造一个函数,它首先处理statusCode
(从记录Status
获取它? - 因为我可以从显示的值Status {statusCode = 200, statusMessage = "OK"}
推断出来),然后将它传递给responseStatus
必须处理响应状态。 所以,反过来说:实际上,状态代码是响应状态的一部分。
第二读对我来说也没有意义,因为它也首先处理状态代码。
r ^. responseStatus . statusCode
的正确读数r ^. responseStatus . statusCode
r ^. responseStatus . statusCode
r ^. responseStatus . statusCode
是r ^. (responseStatus . statusCode)
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
map . first
呢? first
接受一个作用于第一个元素的函数,并返回一个作用于一对的函数,如果我们用这种方式括起这个类型就更明显了:
first :: (a -> b) -> ((a, c) -> (b, c))
另外,回想一下map
的类型:
map :: (a -> b) -> ([a] -> [b])
map
接受一个作用于元素的函数,并返回一个作用于列表的函数。 现在, f . g
f . g
首先应用g
然后将结果输入f
。 所以map . first
map . first
获取一个作用于某个元素类型的函数,将其转换为作用于对的函数,然后将其转换为作用于对列表的函数。
(map . first) :: (a -> b) -> [(a, c)] -> [(b, c)]
first
,将作用于结构一部分的转向函数map
到作用于整个结构的函数。 在map . first
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
尝试忽略Functor
位。 如果我们略微眯眼,这类似于map
和first
的类型。 它的发生使得镜头还将作用于结构部件的功能转换为作用于整个结构的功能。 在上面s
签名中, s
表示整个结构, a
表示它的一部分。 由于我们的输入函数可以改变的类型a
至b
(由作为指示的a -> fb
),我们还需要t
参数“的类型,它粗略意味着s
我们改变后a
到b
里面”。
statusCode
是一个镜头,它将作用于Int
的函数转换为作用于Status
的函数:
statusCode :: Functor f => (Int -> f Int) -> (Status -> f Status)
responseStatus
将作用于Status
的函数转换为作用于Response
的函数:
responseStatus :: Functor f => (Status -> f Status) -> (Response -> f Response)
responseStatus . statusCode
的类型responseStatus . statusCode
responseStatus . statusCode
遵循我们在map . first
看到的相同模式map . first
map . first
:
responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response)
还有待观察^.
作品。 它与镜头的核心机制和魔力密切相关; 我不会在这里重申,因为有很多关于它的着作。 对于介绍我建议看这个和这个 ,你也可以看这个优秀的视频。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.