部分应用< *>带拉链

时间:2017-02-15 22:25:40

标签: haskell functional-programming zip applicative

我开始查看一些Haskell代码并找到:

foo :: ([a] -> [b]) -> [a] -> [(a, b)]
let foo = (<*>) zip

我不明白这是如何运作的,ap期望f (a -> b) -> f azip的类型为[a] -> [b] -> ([a, b])。我了解f a -> f b符合[a] -> [b],但不符合f (a -> b)

1 个答案:

答案 0 :(得分:7)

让我们手工制作出类型。首先,相关表达的类型是什么?

(<*>) :: Applicative f => f (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]

现在,我们需要将zip的类型与(<*>)的第一个参数的类型统一起来。让我们重命名不相关的ab s:

Applicative f => f (a -> b)
                 [c] -> [d] -> [(c, d)]

首先,f是什么?我们的工作是什么?下半部分的类型是一个函数,因此f必须是((->) [c]),或者&#34;函数将c列表作为输入&#34;。一旦我们完成了这项工作,我们就可以看到:

f ~ ((->) [c])
a ~ [d]
b ~ [(c, d)]

现在我们已经有了匹配的类型,我们可以查找(<*>)的函数定义:

instance Applicative ((->) a) where
    pure = const
    (<*>) f g x = f x (g x)

zip替换为f,并将其重写为lambda,产生:

(<*>) zip = \g x -> zip x (g x)

因此,这需要来自[a] -> [b][a]的函数,并使用调用该函数的结果来压缩输入列表。

这在机械上是有道理的,但为什么呢?更一般的理解可以让我们得出这个结论,而不必手工完成所有工作?我不确定我自己对此的解释是否有用,因此如果您不了解正在发生的事情,您可能希望自己研究((->) t)的实例。但如果它有用,这里是一个手工扩展。

((->) t)的Functor,Applicative和Monad实例与Reader t:&#34;具有对类型t&#34;的值的隐式访问权限的函数相同。 (<*>)是关于在Applicative包装器中调用一个函数,对于函数来说,它是一个双参数函数。它安排了&#34;隐含的&#34;将参数传递给f,产生另一个函数,并使用通过将隐式参数传递给g获得的值来调用该函数。因此,对于某些(<*>) ff是&#34;给我另一个函数和值x,我将x传递给f {1}}和另一个功能&#34;。