Haskell如何使用功能组合构成镜头"有可能实现奇怪的参数顺序?

时间:2015-03-08 17:18:07

标签: haskell records higher-order-functions function-composition lenses

我一直在阅读A wreq tutorial

  

镜头提供了一种关注Haskell值的一部分的方法。对于   例如,Response类型有一个responseStatus镜头   重点关注服务器返回的状态信息。

ghci> r ^. responseStatus
Status {statusCode = 200, statusMessage = "OK"}
     

^.运算符将一个值作为其第一个参数,一个镜头作为其参数   第二,并返回镜头所关注值的部分。

     

我们使用功能组合构成镜头,这使我们能够   很容易专注于深层嵌套结构的一部分。

ghci> r ^. responseStatus . statusCode
200

我无法想出用这个参数顺序完成的函数组合如何按顺序处理嵌套结构。

看:r ^. responseStatus . statusCode可以是r ^. (responseStatus . statusCode)(r ^. responseStatus) . statusCode

在第一个中我们构建了一个函数,第一个处理statusCode(从记录Status获取它? - 因为我可以从显示的值中推导出来{ {1}}),然后将其传递给Status {statusCode = 200, statusMessage = "OK"}responseStatus必须处理响应状态。所以,反过来说:实际上,状态代码是响应状态的一部分。

第二读对我来说也没有意义,因为它也首先处理状态代码。

1 个答案:

答案 0 :(得分:13)

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

firstmap都将作用于结构一部分的函数转换为作用于整个结构的函数。在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位。如果我们稍微眯眼,这类似于mapfirst的类型。它的发生使得镜头还将作用于结构部件的功能转换为作用于整个结构的功能。在上面的签名中s表示整个结构,a表示它的一部分。由于我们的输入函数可以将a的类型更改为b(如a -> f b所示),因此我们还需要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的类型遵循与我们map . first相同的模式:

responseStatus . statusCode :: Functor f => (Int -> f Int) -> (Response -> f Response)

还有待观察^.究竟是如何运作的。它与镜片的核心机制和魔力密切相关;我不会在这里重申,因为有很多关于它的着作。如需介绍,我建议您查看this onethis one,还可以观看this excellent video.