<*>如何从纯粹和(>> =)派生?

use*_*628 6 monads haskell functor applicative

class Applicative f => Monad f where
       return :: a -> f a
       (>>=) :: f a -> (a -> f b) -> f b
Run Code Online (Sandbox Code Playgroud)

(<*>)可以源自纯粹和(>>=):

fs <*> as =
    fs >>= (\f -> as >>= (\a -> pure (f a)))
Run Code Online (Sandbox Code Playgroud)

对于线

fs >>= (\f -> as >>= (\a -> pure (f a)))
Run Code Online (Sandbox Code Playgroud)

我很困惑的用法>>=.我认为它需要一个仿函数f a和一个函数,然后返回另一个仿函数f b.但在这个表达中,我感到迷茫.

Ben*_*Ben 8

让我们从我们正在实现的类型开始:

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

(正常类型<*>当然有一个Applicative约束,但在这里我们试图Monad用来实现Applicative)

因此fs <*> as = _,fs是一个"函数f"(f (a -> b)),并且as是"f of as".

我们将从绑定开始fs:

(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
  = fs >>= _
Run Code Online (Sandbox Code Playgroud)

如果您实际编译它,GHC将告诉我们hole(_)具有什么类型:

foo.hs:4:12: warning: [-Wtyped-holes]
    • Found hole: _ :: (a -> b) -> f b
      Where: ‘a’, ‘f’, ‘b’ are rigid type variables bound by
               the type signature for:
                 (Main.<*>) :: forall (f :: * -> *) a b.
                               Monad f =>
                               f (a -> b) -> f a -> f b
               at foo.hs:2:1-45
Run Code Online (Sandbox Code Playgroud)

那讲得通.单子的>>=需要一个f a在左,一个功能a -> f b上的权利,所以通过结合一个f (a -> b)在右边的左边进行函数获取到收到一个(a -> b)"提取"功能的fs.并且假设我们可以编写一个可以使用它来返回的函数f b,那么整个绑定表达式将返回f b我们需要满足的类型签名<*>.

所以它看起来像:

(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
  = fs >>= (\f -> _)
Run Code Online (Sandbox Code Playgroud)

那我们能做什么?我们已经拥有f :: a -> b,而且我们还有as :: f a,我们需要制作一个f b.如果你已经习惯了Functor那么明显; 只是fmap f as.Monad暗示Functor,所以这确实有效:

(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
  = fs >>= (\f -> fmap f as)
Run Code Online (Sandbox Code Playgroud)

我认为,这也是一种容易理解通过Applicative使用设施实现的方式的方法Monad.

那么,为什么使用其他的例子书面>>=pure,而不是仅仅fmap?这是一种倾听回到的日子,Monad没有拥有ApplicativeFunctor为超.Monad总是"道德地"暗示这两者(因为你可以实现ApplicativeFunctor使用它们的特征Monad),但Haskell并不总是要求有这些实例,这导致书籍,教程,博客文章等解释如何实现这些仅使用Monad.给出的示例行简单地用fmapin >>=pure(return)1来概括定义.

我将继续解压缩,就好像我们没有fmap,因此它会导致您感到困惑的版本.

如果我们不打算使用fmap结合f :: a -> bas :: f a,那么我们就需要绑定as,使我们有一种类型的表达式a适用f于:

(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
  = fs >>= (\f -> as >>= (\a -> _))
Run Code Online (Sandbox Code Playgroud)

在那个洞里面我们需要做一个f b,我们有f :: a -> ba :: a.f a给了我们一个b,所以我们需要打电话pure把它变成f b:

(<*>) :: Monad f => f ( a -> b) -> f a -> f b
fs <*> as
  = fs >>= (\f -> as >>= (\a -> pure (f a)))
Run Code Online (Sandbox Code Playgroud)

这就是这条线正在做的事情.

  1. 绑定fs :: f (a -> b)以获取访问权限f :: a -> b
  2. 在可以访问f它的绑定的函数内部as获取访问权限a :: a
  3. 在有权访问的函数内部a(仍然在有权访问的函数内f),调用f amake a b,并调用pure结果使其成为一个f b

1您可以实现fmap使用>>=pureas fmap f xs = xs >>= (\x -> pure (f x)),这也是fmap f xs = xs >>= pure . f.希望您可以看到示例的内部绑定只是内联第一个版本.


Wil*_*ess 5

Applicative 是一个函子。Monad 也是一个函子。我们可以看到“Functorial”值代表其“包含”的计算?产生值(如IO aMaybe a[] a等),作为 ? 各种计算的隐喻。

函子描述 ? 表示概念?计算类型和函数值是“运行”的具体计算?在一个单独的步骤中解释,因此类似于那个著名的附加间接步骤,通过添加据称可以解决任何计算问题。

这两个fsas是你的函子值和绑定(>>=)do符号<-)“中的”函子“变”携带的值。 Bind虽然属于 Monad。

我们可以在 Monad 中实现的内容(return仅用作 的同义词pure

do { f <- fs ;       -- fs >>= ( \ f ->     -- fs  :: F (a -> b)   -- f :: a -> b
     a <- as ;       -- as >>= ( \ a ->     -- as  :: F  a         -- a :: a
     return (f a)    -- return (f a) ) )    -- f a ::         b
   }                                        -- ::     F       b
Run Code Online (Sandbox Code Playgroud)

(或者,使用MonadComprehensions

    [ f a | f <- fs, a <- as ]
Run Code Online (Sandbox Code Playgroud)

),我们从<*>表达相同计算组合的 Applicative 中得到,但没有 Monad 的全部功能。不同之处在于,Applicativeas不依赖于f那里的值,由 表示的计算“产生” fs。一元函子允许这种依赖,

    [ bar x y | x <- xs, y <- foo x ]
Run Code Online (Sandbox Code Playgroud)

但是 Applicative Functors 禁止它。

对于 Applicative,所有“计算”(如fsas)必须“提前”知道;使用 Monad,它们可以被计算——纯粹地——基于先前“计算步骤”的结果(就像foo x这样做:对于计算产生的(每个) x,将(纯粹)计算新的计算,计算将依次产生(一些)(s))。 xs foo xy


如果您想查看>>=表达式中的类型是如何对齐的,这里是您的表达式及其命名的子表达式,因此可以使用它们的类型进行注释,

exp = fs >>= g                                -- fs >>= 
      where  g f = xs >>= h                   --  (\ f -> xs >>=
                   where  h x = return (f x)  --           ( \ x -> pure (f x) ) )

 x   ::    a
 f   ::    a -> b
 f x ::         b
 return (f x) ::      F b
 h   ::    a ->       F b    -- (>>=) :: F a -> (a -> F b) -> F b
 xs  :: F  a                 --          xs     h
                             --           <-----
 xs >>= h ::          F b
 g f ::               F b
 g   ::   (a -> b) -> F b   -- (>>=) :: F (a->b) -> ((a->b) -> F b) -> F b
 fs  :: F (a -> b)          --          fs          g
                            --           <----------
 fs >>= g ::          F b
 exp ::               F b
Run Code Online (Sandbox Code Playgroud)

并且两个(>>=)应用程序的类型适合:

 (fs :: F (a -> b))  >>=  (g :: (a -> b) -> F b)) :: F b
 (xs :: F  a      )  >>=  (h :: (a       -> F b)) :: F b
Run Code Online (Sandbox Code Playgroud)

因此,整体类型确实是

foo :: F (a -> b) -> F a -> F b
foo fs xs = fs >>= g                   -- foo = (<*>)
            where  g f = xs >>= h 
                         where  h x = return (f x)
Run Code Online (Sandbox Code Playgroud)

最后,我们可以将 monadic bind 看作 的一个实现do,并将do符号处理

     do {
Run Code Online (Sandbox Code Playgroud)

抽象地,公理上,由形式的线条组成

           a <- F a ;
           b <- F b ;
           ......
           n <- F n ;
           return (foo a b .... n)
        }
Run Code Online (Sandbox Code Playgroud)

(用aF b等表示相应类型的),这样它描述了类型的整体组合计算,其中。当右侧的表达式都不依赖于前面没有左侧的变量时,它本质上不是 Monadic,而只是该块描述的应用计算。F tfoo :: a -> b -> ... -> n -> t<-do

由于 Monad 定律do,仅用两<-行就足以定义块的含义。对于函子,只<-允许一行 ( fmap f xs = do { x <- xs; return (f x) })。

因此,Functors/Applicative Functors/Monads是 EDSL嵌入式领域特定语言,因为计算描述本身就是我们语言的do符号中箭头右侧的)。


最后,给你一个类型的曼荼罗:

                  T    a
                  T   (a  ->     b)
                      (a  ->  T  b)
                 -------------------
                  T          (T  b)
                 -------------------
                  T              b
Run Code Online (Sandbox Code Playgroud)

这包含三合一:

       F   a                    A    a                  M   a
           a  ->  b             A   (a -> b)                a  ->  M  b
      --------------           --------------          -----------------
       F          b             A         b             M             b
Run Code Online (Sandbox Code Playgroud)