我正在阅读Haskell书中关于monad变换器的内容.
作者提到了以下内容:
Monad怎么样?组成两个具有Monad实例的任意数据类型没有问题.当我们使用Compose with Maybe和list时,我们已经看到了这一点,它们都定义了Monad实例.但是,这样做的结果并没有给你一个Monad.
问题归结为缺乏信息.Compose正在使用的两种类型都是多态的,所以当你尝试为Monad编写bind时,你试图将两个多态绑定组合成一个组合绑定.事实证明,这是不可能的:
Run Code Online (Sandbox Code Playgroud){-# LANGUAGE InstanceSigs #-} -- impossible. instance (Monad f, Monad g) => Monad (Compose f g) where return = pure (>>=) :: Compose f g a -> (a -> Compose f g b) -> Compose f g b (>>=) = ???这些是我们尝试组合的类型,因为并且必然都是具有自己的Monad实例的monad:
Run Code Online (Sandbox Code Playgroud)Monad f => f a -> (a -> f b) -> f b Monad g => g a -> (a -> g b) -> g b从那些,我们正在尝试编写这个绑定:
Run Code Online (Sandbox Code Playgroud)(Monad f, Monad g) => f (g a) -> (a -> f (g b)) -> f (g b)或者换句话说:
Run Code Online (Sandbox Code Playgroud)(Monad f, Monad g) => f (g (f (g a))) -> f (g a)这是不可能的.加入决赛并没有好方法.试图让它发挥作用是一个很好的练习,因为你遇到的障碍本身就具有指导意义.您还可以阅读Mark P. Jones和Luc Duponcheel的Composing monads1,了解为什么它不可能.
我无法理解.他什么意思?究竟什么是monad变压器,它有什么用呢?
作者试图说任何两个单子的构成都是不可能的.这不是因为语言不好,而是因为有monad的组成不是monad.
例如,IsntMonad不是monad:
newtype IsntMonad a = IsntMonad (Maybe (IO a))
instance Monad IsntMonad where
m >>= k = ???
Run Code Online (Sandbox Code Playgroud)
但是,IsMonad是monad:
newtype IsMonad a = IsMonad { runIsMonad :: IO (Maybe a) }
instance Monad IsMonad where
(IsMonad ioma) >>= k = IsMonad $ do
ma <- ioma
case ma of
Just a -> runIsMonad $ k a
Nothing -> return Nothing
Run Code Online (Sandbox Code Playgroud)
因此,monad变形金刚只是一种使构图成为可能的方法.但是,如果我们知道在一般情况下这是不可能的,它是如何工作的?是的,任何两个monad都不可能,但是对于一些具体的monad和任何其他monad来说都是可能的.
因此,有monad可以与任何其他monad组成.在第一近似中的这种单子是单子变换器.
例如,monad Maybe可以与任何其他monad组合:Monad m => m (Maybe a)这样m (Maybe a)的monad也是如此.但是我们如何才能为它做monad实例呢?
instance Monad m => Monad ??? where ...
Run Code Online (Sandbox Code Playgroud)
然后MaybeT显示为帮助器,它被称为monad变换器(后缀T提示).
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
instance Monad m => Monad (MaybeT m) where
m >>= k = MaybeT $ do
ma <- runMaybeT m
case ma of
Just a -> runMaybeT $ k a
Nothing -> return Nothing
Run Code Online (Sandbox Code Playgroud)
在那之后,自动MaybeT IO,MaybeT [],MaybeT STM...是单子.您不需要为它们编写实例.这是他们的好处.
我们知道monad变换器只是组合monad的方式.但它不是一个答案:monad的组成是什么?为什么我们花时间和精力去寻找组合monad的方法?
嗯,正如你所知,任何与某种效果有关的单子.如果我们组成两个monad,我们也会组合它们的效果,这个结果我们将自动获得.
例如:
StateT s Maybe a -- a computation with a state `s` which can fail
ReaderT e Maybe a -- a computation with access to an environment `e` and option to fail
ReaderT e (StateT s Maybe) a -- a computation with state `s`, access to an environment `e` and option to fail
...
Run Code Online (Sandbox Code Playgroud)
monad的组成不是可交换的.例如,MaybeT (State s)与此不一样StateT s Maybe.理解构图将产生什么效果的简单方法是记住效果是从内部monad到外部monad产生的.MaybeT (State s)第一个效果将由State smonad 产生然后由Maybe.但在StateT s Maybe第一个效果将由Maybe当时产生State s.这意味着在失败的情况下StateT s Maybe,我们会丢失状态,但是MaybeT (State s)我们会保存错误发生的状态.
因此,monad的组合是一种在类型级别上构建更复杂monad的简单方法.
如何在实践中应用?例如,让我们的形象情况.你有一些东西和选择来改变它.命令式代码可能如下所示:
thing = defaultValue;
if (transformByOption0)
doTransformationByOption0(thing);
if (transformByOption1)
doTransformationByOption1(thing);
...
Run Code Online (Sandbox Code Playgroud)
在Haskell中我们可以写:
thing <- flip execStateT defaultValue $ do
when transformByOption0 $
modify doTransformationByOption0
when transformByOption1 $
modify doTransformationByOption1
when transformByOption2 $
put =<< doMonadicTransformationByOption2 =<< get
...
Run Code Online (Sandbox Code Playgroud)
这里发生了什么?我在本地包装了一些monad(可能是任何一个)monad State MyThing,这样就可以轻松简单地解决这个问题.这只是十亿个例子中的一个,monad的组合可以很容易地解决你的问题.