“applicative”的意思是什么?

13
当我阅读 Haskell 相关的内容时,有时会遇到形容词“applicative”,但我没有找到足够清晰的定义来解释这个形容词的含义(与 Haskell 的 Applicative 类不同)。我想学习如何识别一段代码/算法/数据结构等是否是“applicative”,就像我可以识别一段是否是“递归”的一样。如果能提供一些对比“applicative”和其他术语(希望这个术语本身更有意义而不仅仅是“非applicative”)的示例,那将非常感谢。
编辑:例如,为什么选择“applicative”这个单词来命名该类,而不是其他单词?这个类有什么特点使得“Applicative”成为它的好名称(即使付出了其含义不明确的代价)?
谢谢!

1
你能举个使用这个术语的页面的例子吗? - ehird
1
以前它被称为“惯用法”; 一个更了解的朋友(Adam Megacz)说他更喜欢“惯用法”而不是“适用函子”(我记得他说“它实际上不是一个函子”)。 - gatoatigrado
任何 Applicative 也是一个 Functor... 只是因为缺乏远见,我们没有 class Functor a => Applicative a - Tom Crockett
1
@pelotom:嗯...我们确实有。它是“Monad”逃脱层次结构的方式。 - ehird
1
@ehird:哎呀,你说得对。我想我的 Haskell 有点生疏了 :) 但是我不明白 gatoatigrado 的朋友是什么意思,他说“它不是真正的函子”。什么不是真正的函子?所有合法的 Applicative 实例都是函子... - Tom Crockett
2个回答

23

在不知道上下文的情况下,“applicative”指的是什么并不清楚。

如果它确实没有指的是适用函子(即Applicative),那么它可能指的是应用本身的形式:f a b c是一种“applicative”形式,这也是适用函子得名的原因:f <$> a <*> b <*> c是类似的。(事实上,习语括号进一步将其联系在一起,让您可以将其写成(| f a b c |)。)

同样,与不基于函数对参数应用(通常以前缀形式出现)的语言相比,“applicative语言”可以进行对比;例如,串联(“基于堆栈”的)语言并非applicative。

要深入回答为什么适用函子被称为它们所说的,请阅读 带效果的适用程序设计。基本思想是很多情况都需要类似于“增强应用”的东西:在某些具有效果的上下文中应用纯函数。比较一下mapmapM的定义:

map :: (a -> b) -> [a] -> [b]
map _ [] = []
map f (x:xs) = f x : map f xs

mapM :: (Monad m) => (a -> m b) -> [a] -> m [b]
mapM _ [] = return []
mapM f (x:xs) = do
  x' <- f x
  xs' <- mapM f xs
  return (x' : xs')

使用mapA(通常称为traverse):

mapA :: (Applicative f) => (a -> f b) -> [a] -> f [b]
mapA _ [] = pure []
mapA f (x:xs) = (:) <$> f x <*> mapA f xs

正如您所看到的,mapA 更加简洁,而且更明显地与 map 相关(如果在 map 中也使用前缀形式的 (:) ,那么就更明显了)。实际上,即使在具有完整Monad的情况下,使用applicative functor符号也很常见,因为它通常更清晰易懂。

查看定义也是有帮助的:

class (Functor f) => Applicative f where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
(<*>) 的类型与应用的类型进行比较:($) :: (a -> b) -> a -> bApplicative 提供了一种通用的“提升”应用程序的形式,使用它的代码以一种applicative风格编写。
更正式地说,正如论文中提到的并由ertes指出的那样,ApplicativeSK combinator calculus的一般化;pureK :: a -> (r -> a)(又名 const)的一般化,而 (<*>)S :: (r -> a -> b) -> (r -> a) -> (r -> b) 的一般化。 r -> a 部分被简单地泛化为 f a;原始类型是通过 ((->) r)Applicative 实例获得的。
作为实际问题,pure 还允许您以更统一的方式编写applicative表达式:pure f <*> effectful <*> pure x <*> effectful,而不是 (\a b -> f a x b) <$> effectful <*> effectful

16

更基本地说,"applicative" 可以理解为在某种形式的SK演算中运作。这也是 Applicative 类的意义所在。它提供了组合子 pure(K的泛化)和 <*>(S的泛化)。

当你的代码以这种风格表达时,就可以称之为 applicative。例如以下代码:

liftA2 (+) sin cos

是一个应用表达式

\x -> sin x + cos x

当然,在Haskell中,Applicative类是应用程序编程的主要构造,但即使在单子或箭头上下文中,你也可以应用式地编写代码:

return (+) `ap` sin `ap` cos

arr (uncurry (+)) . (sin &&& cos)

最后的代码是否是适用的存在争议,因为有人认为使用适用风格需要柯里化才有意义。


4
感谢您指出“applicative”这个术语根源于组合逻辑。+1 - Tom Crockett
4
《应用编程与效应》一书直接指出:“这个类将环境线程化的S和K泛化到了任何效应的线程化上。” - ehird

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接