Haskell:<*>如何发音?

J C*_*per 101 haskell operators

你如何在Applicative类型类中发音这些函数:

(<*>) :: f (a -> b) -> f a -> f b
(*>)  :: f a -> f b -> f b
(<*)  :: f a -> f b -> f a
Run Code Online (Sandbox Code Playgroud)

(也就是说,如果他们不是运营商,他们会被称为什么?)

作为旁注,如果你可以重命名pure为对非数学家更友好的东西,你会怎么称呼它?

C. *_*ann 238

对不起,我真的不懂我的数学,所以我很好奇如何在Applicative类型类中发音

我想,知道你的数学与否,在很大程度上与此无关.正如你可能知道的那样,Haskell从抽象数学的各个领域借用了一些术语,最着名的是类别理论,从那里我们得到了仿函数和monad.在Haskell中使用这些术语在某种程度上与正式的数学定义有所不同,但它们通常足够接近,无论如何都是良好的描述性术语.

Applicative类型的课坐在某处之间FunctorMonad,所以人们期望它有一个类似的数学基础.该Control.Applicative模块的文档以:

该模块描述了仿函数和monad之间的结构:它提供纯表达式和排序,但没有绑定.(从技术上讲,它是一个强大的松散幺半体仿函数.)

嗯.

class (Functor f) => StrongLaxMonoidalFunctor f where
    . . .
Run Code Online (Sandbox Code Playgroud)

Monad我认为不像那些吸引人.

所有这一切基本归结为Applicative与数学上特别有趣的任何概念不对应,因此没有现成的术语可以捕捉它在Haskell中的使用方式.所以,暂时把数学放在一边.


如果我们想知道该怎么称,(<*>)它可能有助于了解它的基本含义.

那么什么是有Applicative,反正,为什么,我们称呼它?

什么Applicative在实践中达是解除的方式任意功能集成到一个Functor.考虑Maybe(可以说是最简单的非平凡Functor)和Bool(最简单的非平凡数据类型)的组合.

maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not
Run Code Online (Sandbox Code Playgroud)

该功能fmap让我们not从工作Bool转向工作Maybe Bool.但是,如果我们想举起(&&)怎么办?

maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)
Run Code Online (Sandbox Code Playgroud)

嗯,那不是我们想要!事实上,它几乎没用.我们可以试着聪明和潜入另一BoolMaybe通过背...

maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)
Run Code Online (Sandbox Code Playgroud)

......但这不好.首先,这是错误的.另一件事,它很难看.我们可以继续尝试,但事实证明,没有办法解除多个参数的函数来处理任意问题Functor.烦!

另一方面,如果我们使用MaybeMonad实例,我们可以轻松地做到:

maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
                  y' <- y
                  return (x' && y')
Run Code Online (Sandbox Code Playgroud)

现在,翻译一个简单的函数是很麻烦的 - 这就是为什么Control.Monad提供一个自动执行它的功能的原因liftM2.名称中的2指的是它适用于两个参数的函数; 3,4和5参数函数存在类似的函数.这些函数更好,但并不完美,指定参数的数量是丑陋和笨拙的.

这将我们带到介绍Applicative类型论文.其中,作者基本上提出了两点意见:

  • 将多参数函数提升为a Functor是非常自然的事情
  • 这样做不需要a的全部功能 Monad

正常功能应用程序是通过简单并置的术语编写的,因此为了使"提升应用程序"尽可能简单和自然,本文介绍了中缀操作符代表应用程序,提升到Functor类型类,以提供所需的内容. .

所有这些都将我们带到了以下几点:(<*>)简单地表示函数应用程序 - 所以为什么发音它的方式与空格"并置运算符"不同?

但是如果这不是很令人满意,我们可以观察到该Control.Monad模块还提供了一个为monad做同样事情的函数:

ap :: (Monad m) => m (a -> b) -> m a -> m b
Run Code Online (Sandbox Code Playgroud)

其中ap,当然,短期为"适用".由于任何Monad可以Applicative,并且ap只需要后者中存在的特征子集,我们可以说如果(<*>)不是运算符,则应该调用它ap.


我们也可以从另一个方向处理事情.在Functor提升操作被称为fmap,因为它是一个泛化map的列表操作.列表上的哪种功能会起作用(<*>)ap当然,列表上有什么东西,但这本身并不是特别有用.

事实上,列表可能有更自然的解释.当您查看以下类型签名时会想到什么?

listApply :: [a -> b] -> [a] -> [b]
Run Code Online (Sandbox Code Playgroud)

对于将列表并行排列的想法非常诱人,将第一个中的每个函数应用到第二个中的相应元素.不幸的是,对于我们的老朋友来说Monad,如果列表的长度不同,这个简单的操作就违反了monad法则.但它很好Applicative,在这种情况下(<*>)成为一种将通用版本串联起来的方式,所以也许我们可以设想调用它zipWithfzipWith


这种拉链式的想法实际上给我们带来了完整 回想一下之前的数学问题,关于幺半群算子?顾名思义,这些是一种结合幺半群和仿函数结构的方法,这两种方法都是熟悉的Haskell类型:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a
Run Code Online (Sandbox Code Playgroud)

如果你把它们放在一个盒子里并且摇晃一下,它们会是什么样子?从Functor我们将保持结构的想法独立于其类型参数,并从Monoid我们将保持功能的整体形式:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ?
    mfAppend :: f ? -> f ? -> f ?
Run Code Online (Sandbox Code Playgroud)

我们不想假设有一种方法来创建一个真正的"空" Functor,我们不能想出一个任意类型的值,所以我们将修复mfEmptyas 的类型f ().

我们也不想强制mfAppend需要一致的类型参数,所以现在我们有:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f ?
Run Code Online (Sandbox Code Playgroud)

结果类型是mfAppend什么?我们有两种我们一无所知的任意类型,所以我们没有很多选择.最明智的做法是保持两者:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f (a, b)
Run Code Online (Sandbox Code Playgroud)

此时mfAppend显然是zipon list 的通用版本,我们可以Applicative轻松地重建:

mfPure x = fmap (\() -> x) mfEmpty
mfApply f x = fmap (\(f, x) -> f x) (mfAppend f x)
Run Code Online (Sandbox Code Playgroud)

这也向我们展示了pure与a的identity元素相关的信息Monoid,因此它的其他好名称可能是任何暗示单位值,空操作等的东西.


这很冗长,总结如下:

  • (<*>) 只是一个修改过的函数应用程序,因此您可以将其读作"ap"或"apply",或者完全按照正常函数应用程序的方式将其删除.
  • (<*>)也粗略地概括zipWith了列表,所以你可以把它读作"zip functors with",类似于阅读fmap"map with functor with".

第一个更接近Applicative类型类的意图- 顾名思义 - 这就是我推荐的.

事实上,我鼓励所有解除应用程序运营商的自由使用和非发音:

  • (<$>),将单参数函数提升为 Functor
  • (<*>),通过一个链接一个多参数函数 Applicative
  • (=<<),它绑定一个进入Monad现有计算的函数

从本质上讲,这三个都只是常规的功能应用,加了一点点.

  • @Colin Cochrane:你确定你没有拼错"啰嗦"吗?:)但是,嘿,我会接受它!我总觉得`Applicative`和它所推广的功能惯用风格没有得到足够的爱,所以我无法抗拒赞美其美德的机会,作为一种解释我(不)发音的方式`( <*>)`. (6认同)
  • 那个Haskell会为`Applicative`提供语法糖!像`[| fabcd |]`(正如原始论文所建议的那样).那么我们就不需要`<*>`组合子了,你可以将这样的表达式称为"函数上下文中的函数应用"的一个例子. (6认同)

BMe*_*eph 21

由于我没有改进CA McCann技术答案的野心,我将解决更为蓬松的问题:

如果你可以重命名pure为像我这样的podunks更友好的东西,你会怎么称呼它?

作为一种替代方案,特别是因为对于版本的持续焦虑和背叛填充没有结束Monad,称为" return",我提出了另一个名称,这表明它的功能可以满足最迫切需要的程序员......最好的功能......好吧,希望每个人都可以抱怨:inject.

拿一个值."注入"成的Functor,Applicative,Monad,或什么都有,你.我投票支持" inject",我批准了这条消息.

  • 我通常倾向于"单位"或"升力"之类的东西,但那些在Haskell中已经有太多其他含义.`inject`是一个很好的名字,可能比我的更好,虽然作为一个小的侧面注释,"注入"用于 - 我认为 - Smalltalk和Ruby用于某种类型的左折方法.我从来不理解这个名字的选择,不过...... (4认同)
  • 这是一个非常老的线程,但我认为使用Ruby&Smalltalk中的`inject`是因为它就像你在列表中的每个元素之间"注入"一个运算符.至少,这就是我一直想到的. (3认同)

Mar*_*oni 6

简单来说:

  • <*>您可以称之为申请。所以,Maybe f <*> Maybe a可以读作申请Maybe fMaybe a

  • 您可以重命名pureof,就像许多JavaScript库一样。在JS,你可以创建一个MaybeMaybe.of(a)

另外,Haskell的Wiki在此处提供有关语言操作员发音的页面