部分应用 <*> 和 zip
Partially applying <*> with zip
我开始查看一些 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)
。
让我们手工算出类型。一、相关表达式的类型是什么?
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]
现在,我们需要统一 zip
的类型和 (<*>)
的第一个参数的类型。让我们重命名不相关的 a
s 和 b
s:
Applicative f => f (a -> b)
[c] -> [d] -> [(c, d)]
首先,什么是f
?我们在做什么应用程序?下半部分的类型是一个函数,所以f
必须是((->) [c])
,或者"functions taking a list of c
as input"。完成后,我们可以看到:
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: "functions which have implicit access to a value of type t
" 相同。 (<*>)
是关于在 Applicative 包装器内部调用一个函数,对于函数来说,它是一个 two-argument 函数。它安排将 "implicit" 参数传递给 f
,生成另一个函数,并使用通过将隐式参数传递给 g
获得的值调用该函数。所以,(<*>) f
,对于某些 f
,是 "Give me another function, and a value x
, and I'll pass x
to both f
and the other function"。
我开始查看一些 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)
。
让我们手工算出类型。一、相关表达式的类型是什么?
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
zip :: [a] -> [b] -> [(a, b)]
现在,我们需要统一 zip
的类型和 (<*>)
的第一个参数的类型。让我们重命名不相关的 a
s 和 b
s:
Applicative f => f (a -> b)
[c] -> [d] -> [(c, d)]
首先,什么是f
?我们在做什么应用程序?下半部分的类型是一个函数,所以f
必须是((->) [c])
,或者"functions taking a list of c
as input"。完成后,我们可以看到:
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: "functions which have implicit access to a value of type t
" 相同。 (<*>)
是关于在 Applicative 包装器内部调用一个函数,对于函数来说,它是一个 two-argument 函数。它安排将 "implicit" 参数传递给 f
,生成另一个函数,并使用通过将隐式参数传递给 g
获得的值调用该函数。所以,(<*>) f
,对于某些 f
,是 "Give me another function, and a value x
, and I'll pass x
to both f
and the other function"。