Den*_*VDB 3 zip haskell functional-programming applicative
我开始查看一些Haskell代码并发现:
foo :: ([a] -> [b]) -> [a] -> [(a, b)]
let foo = (<*>) zip
Run Code Online (Sandbox Code Playgroud)
我不明白这是如何工作的,ap需要一个f (a -> b) -> f a却zip是类型[a] -> [b] -> ([a, b]).我明白这f a -> f b会匹配[a] -> [b],但不会f (a -> b).
让我们手工制作出类型.首先,相关表达的类型是什么?
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]
Run Code Online (Sandbox Code Playgroud)
现在,我们需要zip将第一个参数类型的类型统一到(<*>).让我们重命名不相关的as和bs:
Applicative f => f (a -> b)
[c] -> [d] -> [(c, d)]
Run Code Online (Sandbox Code Playgroud)
首先,是什么f?我们的工作是什么?下半部分的类型是一个函数,因此f必须是((->) [c])"或c作为输入列表的函数".一旦我们完成了,我们可以看到:
f ~ ((->) [c])
a ~ [d]
b ~ [(c, d)]
Run Code Online (Sandbox Code Playgroud)
现在我们已经有了匹配的类型,我们可以查找(<*>)函数的定义:
instance Applicative ((->) a) where
pure = const
(<*>) f g x = f x (g x)
Run Code Online (Sandbox Code Playgroud)
代zip了f这里,并改写为拉姆达收益率:
(<*>) zip = \g x -> zip x (g x)
Run Code Online (Sandbox Code Playgroud)
因此,这需要一个函数from [a] -> [b]和a [a],并将输入列表拉上并调用其上的函数.
这在机械上是有道理的,但为什么?更一般的理解可以让我们得出这个结论,而不必手工完成所有工作?我不确定我自己对此的解释是否有用,所以((->) t)如果您不了解正在发生的事情,您可能想要自己研究这些实例.但是如果它有用,这里是一个手工扩展.
Functor,Applicative和Monad实例((->) t)与Reader t:"隐式访问类型值的函数t"相同.(<*>)是关于在Applicative包装器中调用一个函数,函数是一个双参数函数.它安排将"隐式"参数传递给f,产生另一个函数,并使用通过传递隐式参数获得的值调用该函数g.所以,(<*>) f对某些人来说f,"给我另一个功能,一个价值x,我将传递x给两个f和另一个功能".