class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
据我了解,它使用一个函数f,其中另一个函数(a -> b)
作为其参数返回一个函数f
。将f
应用于a
,然后返回函数f
,并将f
应用于b
。
这里是一个例子:
Prelude> (+) <$> Just 2 <*> Just 3
Just 5
但是我不太了解它是如何工作的。
我猜(+)
应该是f
,Just 2
和Just 3
应该分别是a
和b
。那么(a -> b)
是什么?
答案 0 :(得分:7)
据我了解,它需要一个函数f ...
不幸的是,这是不正确的。在这种情况下,f
是类型,而不是函数。具体来说,f
是类型为* -> *
的“高级类型”。类型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
的通用版本,但这对问题并不重要。)
答案 1 :(得分:1)
应用函子是作为应用样式编程"Idioms" 引入Haskell 的。解开这个短语,我们有“应用样式编程”;这只是函数对参数的应用。我们也有“成语”或具有特殊含义的语言短语。例如,“给猫和狗下雨”是下大雨的习语。应用函子放在一起,是具有特殊含义的函数应用。
例如,按照Dietrich Epp的定义,anApplication
由函数定义,
anApplication = f a
where
f = (+2)
a = 3
和anIdiomaticApplication
(通过惯用应用程序定义)
anIdiomaticApplication = f <*> a
where
f = Just (+2)
a = Just 3
这些定义的顶层结构相似。区别?第一个具有空格-正常功能应用程序-第二个具有<*>
-惯用功能应用程序。这说明<*>
是如何促进应用风格的:只需使用<*>
代替空格即可。
应用<*>
是惯用的,因为它具有的意义不只是纯函数应用。通过说明的方式,在anIdiomaticApplication
中,我们有这样的东西:
f <*> a :: Maybe (Int -> Int) <*> Maybe Int
在此,类型中的<*>
用于表示类型级别的函数*,该函数对应于实际<*>
的签名。我们将<*>
和f
(分别为a
和Maybe (Int -> Int)
)的类型实参应用于类型Maybe Int
。申请后我们有
f <*> a :: Maybe Int
作为中间步骤,我们可以想像
f <*> a :: Maybe ((Int -> Int) _ Int)
其中_
是常规功能应用程序的类型级别替身。
在这一点上,我们终于可以看到习惯用法了。在f <*> a
上下文/习惯用法中,(Int -> Int) _ Int
就像普通功能应用程序Maybe
。因此,<*>
只是在特定上下文中发生的函数应用程序。
在分开时,我将强调理解<*>
仅部分地了解其用法。我们可以理解,f <*> a
只是函数应用程序,具有一些特殊的含义。根据适用法律,我们还可以假定惯用的应用在某种程度上是明智的。
但是,如果您看着<*>
并感到困惑,因为那里的内容很少,请不要感到惊讶。我们还必须精通各种Haskell习语。例如,在Maybe
惯用语中,可能不存在函数或值,在这种情况下,输出将为Nothing
。当然,还有许多其他人,但是仅熟悉Either a
和State s
应该可以对各种各样的不同种类进行建模。
*类似的事情实际上可以由封闭类型的家族(未经测试)
完成type family IdmApp f a where
IdmApp (f (a->b)) a = f b