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.但在这个表达中,我感到迷茫.
让我们从我们正在实现的类型开始:
(<*>) :: 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并没有拥有Applicative和Functor为超.Monad总是"道德地"暗示这两者(因为你可以实现Applicative和Functor使用它们的特征Monad),但Haskell并不总是要求有这些实例,这导致书籍,教程,博客文章等解释如何实现这些仅使用Monad.给出的示例行简单地用fmapin >>=和pure(return)1来概括定义.
我将继续解压缩,就好像我们没有fmap,因此它会导致您感到困惑的版本.
如果我们不打算使用fmap结合f :: a -> b和as :: 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 -> b和a :: 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)
这就是这条线正在做的事情.
fs :: f (a -> b)以获取访问权限f :: a -> bf它的绑定的函数内部as获取访问权限a :: aa(仍然在有权访问的函数内f),调用f amake a b,并调用pure结果使其成为一个f b1您可以实现fmap使用>>=和pureas fmap f xs = xs >>= (\x -> pure (f x)),这也是fmap f xs = xs >>= pure . f.希望您可以看到示例的内部绑定只是内联第一个版本.
Applicative 是一个函子。Monad 也是一个函子。我们可以看到“Functorial”值代表其“包含”的计算?产生纯值(如IO a、Maybe a、[] a等),作为 ? 各种计算的隐喻。
函子描述 ? 表示概念?计算类型和函数值是“运行”的具体计算?在一个单独的步骤中解释,因此类似于那个著名的附加间接步骤,通过添加据称可以解决任何计算问题。
这两个fs和as是你的函子值和绑定((>>=)或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,所有“计算”(如fs或as)必须“提前”知道;使用 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)
(用a、F 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)