部分应用带拉链的<*>

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 azip是类型[a] -> [b] -> ([a, b]).我明白这f a -> f b会匹配[a] -> [b],但不会f (a -> b).

ama*_*loy 7

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

(<*>) :: 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)

zipf这里,并改写为拉姆达收益率:

(<*>) 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和另一个功能".