我试图采取例如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
中}。
所需的属性是M2
和M1
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
声明后,我开始失去与newtype
和StateT A M
类型的连接。
ExceptT ...
现在已经实施的变形金刚消失了,我想我正在尝试做一些没有多大意义的事情。所以我的问题是:如何将这种行为正确地包装到新的复合单子中,使得可以访问下面的层,以避免不必要的提升并保持清晰和井井有条。
答案 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变换器(StateT
,ExceptT
,ContT
等),并为变换器堆栈(MonadX
)派生MyBranchT
实例。< / p>
您通常也会为MyBranchT Identity
创建一个类型同义词,并为runMyBranchT
和runMyBranch
提供函数
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