我开始查看一些Haskell
代码并找到:
foo :: ([a] -> [b]) -> [a] -> [(a, b)]
let foo = (<*>) zip
我不明白这是如何运作的,ap
期望f (a -> b) -> f a
但zip
的类型为[a] -> [b] -> ([a, b])
。我了解f a -> f b
符合[a] -> [b]
,但不符合f (a -> b)
。
答案 0 :(得分:7)
让我们手工制作出类型。首先,相关表达的类型是什么?
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]
现在,我们需要将zip
的类型与(<*>)
的第一个参数的类型统一起来。让我们重命名不相关的a
和b
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
获得的值来调用该函数。因此,对于某些(<*>) f
,f
是&#34;给我另一个函数和值x
,我将x
传递给f
{1}}和另一个功能&#34;。