Haskell:< *>发音? [英] Haskell: How is <*> pronounced?

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

问题描述

 (< *>):: f(a)这些函数在Applicative类型类中如何发音? - > b) - > f a  - > f b 
(*>):: f a - > f b - > f b
(< *):: f a - > f b - > fa

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



请注意,如果您可以将纯粹重命名为对非数学家更友好的事物,您会怎样称呼它?

解决方案


对不起,我不太了解我的数学,所以我很好奇在Applicative类型类中发音函数


在我看来,知道你的数学或许没有多大意义。正如你可能知道的那样,Haskell从抽象数学的各个领域借用了一些术语,特别是类别理论,从那里我们得到函子和单子。这些术语在Haskell中的用法与正式的数学定义有所分歧,但它们通常足够接近,无论如何都是很好的描述性术语。

Applicative type类位于 Functor Monad 之间的某处,所以人们可能会期望它有一个相似的数学基础。 Control.Applicative 模块的文档始于:


该模块描述了一个函子和monad之间的中间结构:它提供纯粹的表达式和排序,但没有约束力。 (技术上来说,这是一个强势松散monoidal函子。)



嗯。

  class(Functor f)=> StrongLaxMonoidalFunctor f其中
。 。 。

Monad 不同,想想。



基本上归结为 Applicative 不符合任何概念,特别是<有趣的在数学上,所以没有现成的术语可以捕捉到它在Haskell中的使用方式。所以,现在就把数学放在一边。






如果我们想知道叫什么( <>)它可能有助于了解它的基本含义。



所以 Applicative ,无论如何,为什么 do 我们称之为?



什么应用是一种将任意函数提升为 Functor 的方法。考虑 Maybe (可以说是最简单的不重要的 Functor )和 Bool code>(同样是最简单的非平凡数据类型)。

  maybeNot :: Maybe Bool  - >也许Bool 
maybeNot = fmap不是

函数 fmap 让我们从 Bool 的工作中解除不是来处理 Maybe Bool 。但是,如果我们要解除(&&)

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

好吧,那不是我们想要的 !事实上,它几乎没用。我们可以尝试聪明地将另一个 Bool 放入 Maybe 后面......

  maybeAnd'':: Maybe Bool  - >布尔 - >也许Bool 
maybeAnd''xy = fmap($ y)(fmap(&& x)x)

...但这并不好。首先,这是错误的。另一件事,它很丑陋。我们可以继续尝试,但事实证明,没有办法解决多个参数的函数来处理任意 Functor 。另一方面,如果我们使用 Maybe 的<$ c $,我们可以轻松地做到这一点c> Monad instance:

  maybeAnd :: Maybe Bool  - >也许Bool  - >可能Bool 
maybeAnd xy = do x'< - x
y'< - y
return(x'&& y')

现在,为了翻译一个简单的函数,这很麻烦 - 这就是为什么 Control.Monad 提供了一个自动执行的函数, liftM2 。名称中的2表示它在两个参数的函数上起作用的事实;对于3,4和5个参数函数存在类似的函数。这些函数更好,但并不完美,并且指定参数的数目是丑陋和笨拙的。



这将我们带入介绍Applicative type class的文章。其中,作者提出了两条观察:


  • 将多参数函数提升为 Functor 是很自然的事情

  • 这样做并不需要 Monad 的完整功能



  • 正常功能应用程序是通过简单的术语并列编写的,因此为了使解除应用程序尽可能简单自然,本文引入了 infix运营商站在应用程序中,提升到 Functor 和一个类型类,以提供所需的功能。



    所有这些都让我们看到以下几点: (< *>)不同于你做空白的并列运算符?

    但是,如果这不是很令人满意,我们可以观察 Control。 Monad 模块也提供了一个相同的功能r monads:

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

    当然, ap 适用的缩写。因为任何 Monad 都可以是 Applicative ,并且 ap 只需要在后者中存在的特征子集,我们可以说 if(code>(*))不是一个运算符,它应该被称为 ap






    我们也可以从其他方向。 Functor 提升操作称为 fmap ,因为它是 map 列表中的操作。列表上的什么样的函数可以像(<>)一样工作?当然,列表中有 ap ,但这并不是特别有用。



    事实上,列表可能有更自然的解释。当你看到下面的类型签名时想到什么?

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

    并列列表的想法非常诱人,功能在第一个到相应的第二个元素。不幸的是,对于我们的老朋友 Monad ,如果列表长度不同,这个简单的操作违反了monad定律。但是,在(<*>)成为 的一种方式时,它会生成 Applicative zipWith 的广义版本串在一起,因此我们可以想象将它称为 fzipWith






    这个压缩的想法实际上给我们带来了完整的圆圈。回想一下数学的东西,关于monoidal函数?顾名思义,这些方法就是将monoids和functor的结构相结合,两者都是熟悉的Haskell类型类:

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

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

    如果您将它们放在一个盒子中并将它摇动一下,这些看起来会是什么样子?从 Functor ,我们将保持独立于其类型参数结构的概念,并从 Monoid 我们将保留函数的整体形式:

      class(Functor f)=> MonoidalFunctor f其中
    mfEmpty :: f?
    mfAppend :: f? - > F ? - > F ?

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



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

    $ p $ class(Functor f)=> MonoidalFunctor f其中
    mfEmpty :: f()
    mfAppend :: f a - > f b - > F ?

    mfAppend 的结果类型是什么?我们有两种任意类型的任意类型,所以我们没有太多的选择。最明智的就是保持两者:

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

    mfAppend 现在显然是列表中的 zip 的通用版本,我们可以轻松地重建 Applicative



    pre $ mfPure x = fmap(\() - > x)mfEmpty
    mfApply fx = fmap(\(f,x ) - > fx)(mfAppend fx)

    这也显示 Monoid 的标识元素相关,所以其他好的名称可能是任何暗示单位值,空操作或这样。




    这段时间太长了,所以总结一下:


    • (*)只是一个修改后的函数应用程序,所以您可以将其读为ap或apply ,或者完全按照您正常运行应用程序的方式将其删除。
    • >也粗略地概括了列表中的 zipWith ,所以你可以把它看作zip functors with,类似于阅读 fmap 作为映射仿函数。


    第一个更接近应用类型类 - 顾名思义 - 这就是我的建议。



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


    • (< $>) ,它将单参数函数提升为 Functor

    • (< ; *>),它链接了一个多参数func通过 Applicative

    • (=<< /< c $ c>将一个输入 Monad 的函数绑定到一个已有的计算上



    ,在内心,只是普通的功能应用程序,加点点儿。

    How do you pronounce these functions in the Applicative typeclass:

    (<*>) :: 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?)

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

    解决方案

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

    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.

    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:

    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.)

    Hmm.

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

    Not quite as catchy as Monad, I think.

    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.

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

    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
    

    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 (&&)
    

    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)
    

    ...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!

    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')
    

    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.

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

    • 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

    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"?

    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
    

    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.


    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]
    

    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?


    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
    

    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 ?
    

    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 ().

    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 ?
    

    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)
    

    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)
    

    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.


    That was lengthy, so to summarize:

    • (<*>) 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".

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

    In fact, I encourage liberal use, and non-pronunciation, of all lifted application operators:

    • (<$>), 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.

    这篇关于Haskell:&lt; *&gt;发音?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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