(<*>) :: f (a -> b) -> f a -> f b 在 Functor class 中究竟做了什么
what does (<*>) :: f (a -> b) -> f a -> f b exactly do in the Functor class
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
根据我的理解,它需要一个函数 f,其中另一个函数 (a -> b)
作为它的参数,returns 一个函数 f
。将 f
应用到 a
然后 returns 一个函数 f
并将 f
应用到 b
.
这是一个例子:
Prelude> (+) <$> Just 2 <*> Just 3
Just 5
但是我不太明白它是如何工作的。
我猜(+)
应该是f
,Just 2
和Just 3
应该分别是a
和b
。那什么是(a -> b)
呢?
From my understanding, it takes a function f...
不幸的是,这是不正确的。在这种情况下,f
是一个类型,而不是一个函数。具体来说,f
是种类 * -> *
的 "higher-kinded type"。类型 f
是函子。
在这种情况下,f
是 Maybe
。所以我们可以重写函数类型,将它们专门化为 Maybe
:
pure :: a -> Maybe a
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
一旦你走到这一步,它就会开始变得有点清晰。 pure
有几个不同的可能定义,但只有一个有意义:
pure = Just
运算符x <$> y
和pure x <*> y
是一样的,所以如果写成:
(+) <$> Just 2 <*> Just 3
那么我们可以改写为:
pure (+) <*> pure 2 <*> pure 3
虽然这在技术上有更通用的类型。使用函子定律,我们知道 pure x <*> pure y
与 pure (x y)
相同,所以我们得到
pure ((+) 2) <*> pure 3
pure ((+) 2 3)
pure (2 + 3)
在这种情况下,我们 a
和 b
是 类型 但是由于 <*>
出现了两次,它们实际上在每个中都有不同的类型案例.
在第一个<*>
中,a
是Int
,b
是Int -> Int
。
在第二个<*>
中,a
和b
都是Int
。 (从技术上讲,您可以获得 Int
的通用版本,但这对问题来说并不重要。)
应用仿函数被引入 Haskell 作为应用风格编程 "Idioms"。解包这个短语,我们有 "applicative style programming";这只是将函数应用于参数。我们还有 "idioms" 或具有特殊含义的语言短语。例如 "raining cats and dogs" 是表示下大雨的成语。把它们放在一起,applicative functor就是具有特殊意义的函数应用。
举个例子,在 Dietrich Epp 的带领下,anApplication
由函数定义,
anApplication = f a
where
f = (+2)
a = 3
并且,anIdiomaticApplication
,用惯用的应用程序定义,
anIdiomaticApplication = f <*> a
where
f = Just (+2)
a = Just 3
这些定义的顶层结构相似。区别?第一个有 space——正常的函数应用——第二个有 <*>
——惯用的函数应用。这说明了 <*>
如何促进应用风格:只需使用 <*>
代替 space.
应用程序 <*>
是惯用的,因为它具有不仅仅是纯功能应用程序的含义。作为说明,在 anIdiomaticApplication
中我们有这样的东西:
f <*> a :: Maybe (Int -> Int) <*> Maybe Int
这里,type中的<*>
用来表示一个类型级别的函数*,对应真实<*>
的签名。对于 type-<*>
,我们应用 f
和 a
的类型参数(分别为 Maybe (Int -> Int)
和 Maybe Int
)。申请后我们有
f <*> a :: Maybe Int
作为中间步骤,我们可以想象这样的事情
f <*> a :: Maybe ((Int -> Int) _ Int)
_
是常规函数应用程序的类型级别替代。
到这里终于可以看到成语的叫法了。 f <*> a
就像一个普通的函数应用程序,(Int -> Int) _ Int
,在Maybe
context/idiom 中。因此,<*>
只是在特定上下文中发生的函数应用。
临别,我要强调的是理解<*>
只是部分理解它的使用。我们可以理解为 f <*> a
只是一些额外的惯用意义的函数应用。由于应用法则,我们还可以假设惯用应用在某种程度上是明智的。
但是,如果您看到 <*>
并因为内容太少而感到困惑,请不要感到惊讶。我们还必须精通各种 Haskell 成语。例如,在 Maybe
习语中,函数或值可能不存在,在这种情况下输出将是 Nothing
。当然还有很多其他的,但是只要熟悉 Either a
和 State s
就可以模拟各种不同的类型。
*像这样的东西实际上可以用一个封闭的类型系列来制作(未经测试)
type family IdmApp f a where
IdmApp (f (a->b)) a = f b
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
根据我的理解,它需要一个函数 f,其中另一个函数 (a -> b)
作为它的参数,returns 一个函数 f
。将 f
应用到 a
然后 returns 一个函数 f
并将 f
应用到 b
.
这是一个例子:
Prelude> (+) <$> Just 2 <*> Just 3
Just 5
但是我不太明白它是如何工作的。
我猜(+)
应该是f
,Just 2
和Just 3
应该分别是a
和b
。那什么是(a -> b)
呢?
From my understanding, it takes a function f...
不幸的是,这是不正确的。在这种情况下,f
是一个类型,而不是一个函数。具体来说,f
是种类 * -> *
的 "higher-kinded type"。类型 f
是函子。
在这种情况下,f
是 Maybe
。所以我们可以重写函数类型,将它们专门化为 Maybe
:
pure :: a -> Maybe a
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
一旦你走到这一步,它就会开始变得有点清晰。 pure
有几个不同的可能定义,但只有一个有意义:
pure = Just
运算符x <$> y
和pure x <*> y
是一样的,所以如果写成:
(+) <$> Just 2 <*> Just 3
那么我们可以改写为:
pure (+) <*> pure 2 <*> pure 3
虽然这在技术上有更通用的类型。使用函子定律,我们知道 pure x <*> pure y
与 pure (x y)
相同,所以我们得到
pure ((+) 2) <*> pure 3
pure ((+) 2 3)
pure (2 + 3)
在这种情况下,我们 a
和 b
是 类型 但是由于 <*>
出现了两次,它们实际上在每个中都有不同的类型案例.
在第一个<*>
中,a
是Int
,b
是Int -> Int
。
在第二个<*>
中,a
和b
都是Int
。 (从技术上讲,您可以获得 Int
的通用版本,但这对问题来说并不重要。)
应用仿函数被引入 Haskell 作为应用风格编程 "Idioms"。解包这个短语,我们有 "applicative style programming";这只是将函数应用于参数。我们还有 "idioms" 或具有特殊含义的语言短语。例如 "raining cats and dogs" 是表示下大雨的成语。把它们放在一起,applicative functor就是具有特殊意义的函数应用。
举个例子,在 Dietrich Epp 的带领下,anApplication
由函数定义,
anApplication = f a
where
f = (+2)
a = 3
并且,anIdiomaticApplication
,用惯用的应用程序定义,
anIdiomaticApplication = f <*> a
where
f = Just (+2)
a = Just 3
这些定义的顶层结构相似。区别?第一个有 space——正常的函数应用——第二个有 <*>
——惯用的函数应用。这说明了 <*>
如何促进应用风格:只需使用 <*>
代替 space.
应用程序 <*>
是惯用的,因为它具有不仅仅是纯功能应用程序的含义。作为说明,在 anIdiomaticApplication
中我们有这样的东西:
f <*> a :: Maybe (Int -> Int) <*> Maybe Int
这里,type中的<*>
用来表示一个类型级别的函数*,对应真实<*>
的签名。对于 type-<*>
,我们应用 f
和 a
的类型参数(分别为 Maybe (Int -> Int)
和 Maybe Int
)。申请后我们有
f <*> a :: Maybe Int
作为中间步骤,我们可以想象这样的事情
f <*> a :: Maybe ((Int -> Int) _ Int)
_
是常规函数应用程序的类型级别替代。
到这里终于可以看到成语的叫法了。 f <*> a
就像一个普通的函数应用程序,(Int -> Int) _ Int
,在Maybe
context/idiom 中。因此,<*>
只是在特定上下文中发生的函数应用。
临别,我要强调的是理解<*>
只是部分理解它的使用。我们可以理解为 f <*> a
只是一些额外的惯用意义的函数应用。由于应用法则,我们还可以假设惯用应用在某种程度上是明智的。
但是,如果您看到 <*>
并因为内容太少而感到困惑,请不要感到惊讶。我们还必须精通各种 Haskell 成语。例如,在 Maybe
习语中,函数或值可能不存在,在这种情况下输出将是 Nothing
。当然还有很多其他的,但是只要熟悉 Either a
和 State s
就可以模拟各种不同的类型。
*像这样的东西实际上可以用一个封闭的类型系列来制作(未经测试)
type family IdmApp f a where
IdmApp (f (a->b)) a = f b