整理Monad - 将monad变换器的应用转换为newtype monad

时间:2015-09-14 19:09:45

标签: haskell monad-transformers state-monad newtype lifting

我试图采取例如angular.module('myApp').directive('directiveA', function () { return { templateUrl: 'directiveA.html', link: function (scope) { //code here }, controller: function($scope){ scope.directiveAAlert = function () { alert('This is A'); } } } }); angular.module('myApp').directive('directiveB', function () { return { scope: { onButtonClicked: '=' }, require: '^directiveA', templateUrl: 'directiveB.html', link: function (scope,element,attrs, ctrl) { scope.showAlert = function () { ctrl.onButtonClicked(); //ctrl has access to the directive a controller. } } } }); ,对于某些具体类型ExceptT a (StateT A M)和monad A,并将它们包装到我的新自定义monad中。

首先我发现M经常出现在其他情境中,因此我认为最好将其单独包含在monad StateT A M中,然后将M1包装到ExceptT a M1中}。

所需的属性是M2M1 M2实例和MonadState类(假设它被称为M)。 MyMonadClass也应该是M2的实例。

首先,我从简单类型的同义词开始:

MonadError

然后我想我会首先勾画实例声明(没有实现实例),这就是我第一次被卡住的地方。 type MyState = StateT A M type MyBranch a = ExceptT a MyState 似乎不是正确的语法。我想我必须创建instance MonadState A (MyState)然后newtype MyState' a = StateT a M(不要在没有必要的地方使用语言扩展名)。

但是,当我开始将同义词转换为type MyState = MyState A声明后,我开始失去与newtypeStateT A M类型的连接。

ExceptT ...

现在已经实施的变形金刚消失了,我想我正在尝试做一些没有多大意义的事情。所以我的问题是:如何将这种行为正确地包装到新的复合单子中,使得可以访问下面的层,以避免不必要的提升并保持清晰和井井有条。

1 个答案:

答案 0 :(得分:10)

正常模式是为整个变换器堆栈定义一个新类型。

data A = A
data E = E

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }

如果堆栈中有任何部分可以自行添加有意义的新功能,那么您还可以为这些部分定义新类型。

然后使用GeneralizedNewtypeDeriving获取所有各种monad类的实例。

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- base
import Control.Applicative
import Control.Monad

-- transformers
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.State.Lazy

-- mtl classes
import Control.Monad.Cont.Class
import Control.Monad.Error.Class
import Control.Monad.Reader.Class
import Control.Monad.State.Class
import Control.Monad.Writer.Class

data A = A
data E = E

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }
    deriving (Functor, Applicative, Monad, MonadIO,     -- classes from base and transformers
              {- Alternative, MonadPlus, -}             -- if E is a monoid
              MonadState A, MonadError E,               -- classes from mtl that MyBranchT provides
              MonadCont, MonadReader r, MonadWriter w)  -- classes from mtl that might be available from m

您必须手动编写MonadTrans实例。对于堆栈中的每个变换器,lift总是只有lift一次。

instance MonadTrans MyBranchT where
    lift = MyBranchT . lift . lift

如果堆栈中有任何部分可以自行添加有意义的新功能X,那么您还要为这些功能定义一个新的MonadX类,为每个功能编写MonadX个实例如果可能的话,monad变换器(StateTExceptTContT等),并为变换器堆栈(MonadX)派生MyBranchT实例。< / p>

您通常也会为MyBranchT Identity创建一个类型同义词,并为runMyBranchTrunMyBranch提供函数

import Data.Functor.Identity

type MyBranch a = MyBranchT Identity a

runMyBranchT :: MyBranchT m a -> A -> m (Either E a, A)
runMyBranchT mb s = runStateT (runExceptT (getMyBranchT mb)) s

runMyBranch :: MyBranch a -> A -> (Either E a, A)
runMyBranch mb s = runIdentity $ runMyBranchT mb s