什么是<*>调用它做什么? [英] What is <*> called and what does it do?

查看:20
本文介绍了什么是<*>调用它做什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Applicative 类型类中的这些函数是如何工作的?

How do these functions in the Applicative typeclass work?

(<*>) :: f (a -> b) -> f a -> f b
(*>)  :: f a -> f b -> f b
(<*)  :: f a -> f b -> f a

(也就是说,如果他们不是操作员,他们会叫什么?)

(That is, if they weren't operators, what might they be called?)

顺便说一句,如果您可以将 pure 重命名为对非数学家更友好的名称,您会怎么称呼它?

As a side note, if you could rename pure to something more friendly to non-mathematicians, what would you call it?

推荐答案

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

Sorry, I don't really know my math, so I'm curious how to pronounce the functions in the Applicative typeclass

我认为,了解或不了解您的数学在这里基本上无关紧要.您可能知道,Haskell 从抽象数学的各个领域借用了一些术语,最著名的是 范畴论,我们从那里得到函子和单子.这些术语在 Haskell 中的使用与正式的数学定义有所不同,但它们通常足够接近,无论如何都是很好的描述性术语.

Knowing your math, or not, is largely irrelevant here, I think. As you're probably aware, Haskell borrows a few bits of terminology from various fields of abstract math, most notably Category Theory, from whence we get functors and monads. The use of these terms in Haskell diverges somewhat from the formal mathematical definitions, but they're usually close enough to be good descriptive terms anyway.

Applicative 类型类位于 FunctorMonad 之间,因此人们希望它具有相似的数学基础.Control.Applicative 模块的文档开始于:

The Applicative type class sits somewhere between Functor and Monad, so one would expect it to have a similar mathematical basis. The documentation for the Control.Applicative module begins with:

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

This module describes a structure intermediate between a functor and a monad: it provides pure expressions and sequencing, but no binding. (Technically, a strong lax monoidal functor.)

嗯.

class (Functor f) => StrongLaxMonoidalFunctor f where
    . . .

我认为不如 Monad 那样朗朗上口.

Not quite as catchy as Monad, I think.

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

What all this basically boils down to is that Applicative doesn't correspond to any concept that's particularly interesting mathematically, so there's no ready-made terms lying around that capture the way it's used in Haskell. So, set the math aside for now.

如果我们想知道如何调用 (<*>),那么了解它的基本含义可能会有所帮助.

If we want to know what to call (<*>) it might help to know what it basically means.

无论如何,Applicative 是怎么回事,我们为什么要这样做?

So what's up with Applicative, anyway, and why do we call it that?

Applicative 在实践中的意义是一种将任意函数提升为Functor的方法.考虑Maybe(可以说是最简单的非平凡Functor)和Bool(同样是最简单的非平凡数据类型)的组合.

What Applicative amounts to in practice is a way to lift arbitrary functions into a Functor. Consider the combination of Maybe (arguably the simplest non-trivial Functor) and Bool (likewise the simplest non-trivial data type).

maybeNot :: Maybe Bool -> Maybe Bool
maybeNot = fmap not

函数fmap 让我们将notBool 提升到Maybe Bool.但是如果我们想要提升 (&&) 呢?

The function fmap lets us lift not from working on Bool to working on Maybe Bool. But what if we want to lift (&&)?

maybeAnd' :: Maybe Bool -> Maybe (Bool -> Bool)
maybeAnd' = fmap (&&)

好吧,这不是我们想要的!事实上,它几乎没有用.我们可以聪明一点,从后面偷偷把另一个Bool插入到Maybe中...

Well, that's not what we want at all! In fact, it's pretty much useless. We can try to be clever and sneak another Bool into Maybe through the back...

maybeAnd'' :: Maybe Bool -> Bool -> Maybe Bool
maybeAnd'' x y = fmap ($ y) (fmap (&&) x)

...但这不好.一方面,这是错误的.另一方面,它.我们可以继续尝试,但结果是无法提升具有多个参数的函数来处理任意 Functor.烦人!

...but that's no good. For one thing, it's wrong. For another thing, it's ugly. We could keep trying, but it turns out that there's no way to lift a function of multiple arguments to work on an arbitrary Functor. Annoying!

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

On the other hand, we could do it easily if we used Maybe's Monad instance:

maybeAnd :: Maybe Bool -> Maybe Bool -> Maybe Bool
maybeAnd x y = do x' <- x
                  y' <- y
                  return (x' && y')

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

Now, that's a lot of hassle just to translate a simple function--which is why Control.Monad provides a function to do it automatically, liftM2. The 2 in its name refers to the fact that it works on functions of exactly two arguments; similar functions exist for 3, 4, and 5 argument functions. These functions are better, but not perfect, and specifying the number of arguments is ugly and clumsy.

这将我们带到了介绍 Applicative 类型类的论文.在其中,作者主要进行了两个观察:

Which brings us to the paper that introduced the Applicative type class. In it, the authors make essentially two observations:

  • 将多参数函数提升为 Functor 是很自然的事情
  • 这样做不需要 Monad
  • 的全部功能
  • Lifting multi-argument functions into a Functor is a very natural thing to do
  • Doing so doesn't require the full capabilities of a Monad

普通函数应用是通过简单的词条并列来写的,为了让提升应用"尽可能简单自然,本文引入了中缀操作符来代替应用,提升到Functor,以及提供所需内容的类型类.

Normal function application is written by simple juxtaposition of terms, so to make "lifted application" as simple and natural as possible, the paper introduces infix operators to stand in for application, lifted into the Functor, and a type class to provide what's needed for that.

所有这些都使我们得出以下观点:(<*>) 仅代表函数应用程序——那么为什么它的发音与空格并列运算符"不同?

All of which brings us to the following point: (<*>) simply represents function application--so why pronounce it any differently than you do the whitespace "juxtaposition operator"?

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

But if that's not very satisfying, we can observe that the Control.Monad module also provides a function that does the same thing for monads:

ap :: (Monad m) => m (a -> b) -> m a -> m b

ap 当然是apply"的缩写.由于任何Monad 都可以是Applicative,而ap 只需要后者中存在的特征子集,我们也许可以说如果(<*>) 不是操作符,应该叫ap.

Where ap is, of course, short for "apply". Since any Monad can be Applicative, and ap needs only the subset of features present in the latter, we can perhaps say that if (<*>) weren't an operator, it should be called ap.

我们也可以从另一个方向处理事情.Functor 提升操作被称为 fmap 因为它是对列表的 map 操作的概括.列表上的哪种函数会像 (<*>) 那样工作?当然,ap 在列表上有什么作用,但它本身并不是特别有用.

We can also approach things from the other direction. The Functor lifting operation is called fmap because it's a generalization of the map operation on lists. What sort of function on lists would work like (<*>)? There's what ap does on lists, of course, but that's not particularly useful on its own.

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

In fact, there's a perhaps more natural interpretation for lists. What comes to mind when you look at the following type signature?

listApply :: [a -> b] -> [a] -> [b]

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

There's something just so tempting about the idea of lining the lists up in parallel, applying each function in the first to the corresponding element of the second. Unfortunately for our old friend Monad, this simple operation violates the monad laws if the lists are of different lengths. But it makes a fine Applicative, in which case (<*>) becomes a way of stringing together a generalized version of zipWith, so perhaps we can imagine calling it fzipWith?

这个压缩的想法实际上给我们带来了完整的循环.还记得之前关于幺半群函子的数学内容吗?顾名思义,这是一种组合幺半群和函子结构的方式,这两者都是熟悉的 Haskell 类型类:

This zipping idea actually brings us full circle. Recall that math stuff earlier, about monoidal functors? As the name suggests, these are a way of combining the structure of monoids and functors, both of which are familiar Haskell type classes:

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

class Monoid a where
    mempty :: a
    mappend :: a -> a -> a

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

What would these look like if you put them in a box together and shook it up a bit? From Functor we'll keep the idea of a structure independent of its type parameter, and from Monoid we'll keep the overall form of the functions:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ?
    mfAppend :: f ? -> f ? -> f ?

我们不想假设有一种方法可以创建一个真正空"的 Functor,并且我们无法想象出任意类型的值,因此我们将修复mfEmpty 的类型为 f().

We don't want to assume that there's a way to create an truly "empty" Functor, and we can't conjure up a value of an arbitrary type, so we'll fix the type of mfEmpty as f ().

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

We also don't want to force mfAppend to need a consistent type parameter, so now we have this:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f ?

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

What's the result type for mfAppend? We have two arbitrary types we know nothing about, so we don't have many options. The most sensible thing is to just keep both:

class (Functor f) => MonoidalFunctor f where
    mfEmpty :: f ()
    mfAppend :: f a -> f b -> f (a, b)

此时 mfAppend 现在显然是列表上 zip 的通用版本,我们可以轻松重构 Applicative:

At which point mfAppend is now clearly a generalized version of zip on lists, and we can reconstruct Applicative easily:

mfPure x = fmap (() -> x) mfEmpty
mfApply f x = fmap ((f, x) -> f x) (mfAppend f x)

这也向我们展示了 pureMonoid 的标识元素相关,所以它的其他好名字可能是任何暗示单位值、空操作的东西,或诸如此类.

This also shows us that pure is related to the identity element of a Monoid, so other good names for it might be anything suggesting a unit value, a null operation, or such.

那太长了,总结一下:

  • (<*>) 只是一个修改过的函数应用程序,因此您可以将其读作ap"或apply",也可以完全省略它正常功能应用.
  • (<*>) 也粗略地概括了列表上的 zipWith,因此您可以将其读作zip functors with",类似于阅读 fmap 作为映射函子".
  • (<*>) is just a modified function application, so you can either read it as "ap" or "apply", or elide it entirely the way you would normal function application.
  • (<*>) also roughly generalizes zipWith on lists, so you can read it as "zip functors with", similarly to reading fmap as "map a functor with".

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

The first is closer to the intent of the Applicative type class--as the name suggests--so that's what I recommend.

事实上,我鼓励自由使用所有提升的应用程序运算符,不要发音:

  • (<$>),将单参数函数提升为Functor
  • (<*>),它通过一个 Applicative
  • 链接一个多参数函数
  • (=<<),它将输入Monad的函数绑定到现有计算上
  • (<$>), which lifts a single-argument function into a Functor
  • (<*>), which chains a multi-argument function through an Applicative
  • (=<<), which binds a function that enters a Monad onto an existing computation

从本质上讲,这三个都只是普通的函数应用程序,稍微加了点料.

All three are, at heart, just regular function application, spiced up a little bit.

这篇关于什么是&lt;*&gt;调用它做什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆