2013-07-30 50 views
21

FreeT/ProgramT创建的monad变换器可以有类似于mtl的机制吗?Monad堆栈渗透类与免费/操作Monad变压器?

我对历史的理解如下。曾几何时,monad变压器被发明出来。然后人们开始将monad变压器堆叠在一起,然后发现在任何地方插入lift都很烦人。然后有几个人发明了monad类,以便我们可以例如ask :: m r in any monad m such as MonadReader r m。这是可能通过使每个单子类渗透每一个单子转换,像

(Monoid w, MonadState s m) => MonadState s (WriterT w m)
MonadWriter w m => MonadWriter w (StateT s m)

你需要对这样的实例声明的每对单子变压器,所以当有ñ单子变形金刚有ñ^2费用。然而,这并不是一个大问题,因为人们大多会使用预定义的单子,很少创建自己的单子。到目前为止,我明白这个故事,还有一些细节,例如在以下Q &答:

Avoiding lift with Monad Transformers

然后我的问题是与新自由单子http://hackage.haskell.org/package/free和运营单子http://hackage.haskell.org/package/operational。它们允许我们编写我们自己的DSL并将其用作monad,只需将语言定义为某种代数data类型(Operational甚至不需要Functor实例)。好消息是我们可以免费获得monads和monad变形金刚;那么monad课程呢?坏消息是,“我们很少定义我们自己的monad变压器”的假设不再成立。

作为试图理解这个问题,我做了两个ProgramT s,使他们互相渗透;

https://github.com/nushio3/practice/blob/master/operational/exe-src/test-05.hs

operational包不支持单子类,所以我又实现minioperational,并修改了它的工作,因为我需要; https://github.com/nushio3/minioperational

不过,我需要专门的实例声明

instance (Monad m, Operational ILang m) => Operational ILang (ProgramT SLang m) where

因为以下形式的一般性声明导致不可判定的实例。

instance (Monad m, Operational f m) => Operational f (ProgramT g m) where

我的问题是,我们怎样才能使它更容易让我们的业务单子互相渗透。或者,我希望能够渗透任何行动不便的单身汉。

我也想知道正确的技术术语,渗透 :)

回答

6

我尝试了一下不同的方法,这使得至少部分答案。由于堆叠monad有时可能会有问题,并且我们知道我们所有的monad都是从某种数据类型构建的,所以我试着将这些数据类型组合起来。

我对MonadFree感觉更舒服,所以我使用了它,但我想也可以使用类似的方法来使用Operational

让我们开始我们的数据类型定义:

{-# LANGUAGE DeriveFunctor, FlexibleContexts, 
      FlexibleInstances, FunctionalDependencies #-} 
import Control.Monad 
import Control.Monad.Free 

data SLang x = ReadStr (String -> x) | WriteStr String x 
    deriving Functor 
data ILang x = ReadInt (Int -> x) | WriteInt Int x 
    deriving Functor 

为了两个仿函数用于在自由单子使用它们结合在了一起,让我们定义它们的副产品:

data EitherF f g a = LeftF (f a) | RightF (g a) 
    deriving Functor 

如果我们通过EitherF f g创建一个免费的monad,我们可以调用它们两个的命令。为了使这一过程透明,我们可以使用MPTC允许从每个仿函数转换成目标之一:

class Lift f g where 
    lift :: f a -> g a 
instance Lift f f where 
    lift = id 

instance Lift f (EitherF f g) where 
    lift = LeftF 
instance Lift g (EitherF f g) where 
    lift = RightF 

现在我们只需要调用lift和转换任一部分进入副产品。

在一名助手功能

wrapLift :: (Functor g, Lift g f, MonadFree f m) => g a -> m a 
wrapLift = wrap . lift . fmap return 

我们终于可以创建通用的功能,使我们能够调用从什么我们可以提升到一个函子命令:

readStr :: (Lift SLang f, MonadFree f m) => m String 
readStr = wrapLift $ ReadStr id 

writeStr :: (Lift SLang f, MonadFree f m) => String -> m() 
writeStr x = wrapLift $ WriteStr x() 

readInt :: (Lift ILang f, MonadFree f m) => m Int 
readInt = wrapLift $ ReadInt id 

writeInt :: (Lift ILang f, MonadFree f m) => Int -> m() 
writeInt x = wrapLift $ WriteInt x() 

然后,该程序可以表达as

myProgram :: (Lift ILang f, Lift SLang f, MonadFree f m) => m() 
myProgram = do 
    str <- readStr 
    writeStr "Length of that str is" 
    writeInt $ length str 
    n <- readInt 
    writeStr "you wanna have it n times; here we go:" 
    writeStr $ replicate n 'H' 

没有定义任何进一步的实例。


尽管以上所有方法都很好地工作,但问题在于如何一般地运行这样的自由单体。我不知道是否有可能拥有完全通用的可组合解决方案。

如果我们只有一个基地函子,我们可以运行它作为

runSLang :: Free SLang x -> String -> (String, x) 
runSLang = f 
    where 
    f (Pure x)    s = (s, x) 
    f (Free (ReadStr g)) s = f (g s) s 
    f (Free (WriteStr s' x)) _ = f x s' 

如果我们有两个,我们需要线程他们两人的状态:

runBoth :: Free (EitherF SLang ILang) a -> String -> Int -> ((String, Int), a) 
runBoth = f 
    where 
    f (Pure x)      s i = ((s, i), x) 
    f (Free (LeftF (ReadStr g)))  s i = f (g s) s i 
    f (Free (LeftF (WriteStr s' x))) _ i = f x s' i 
    f (Free (RightF (ReadInt g)))  s i = f (g i) s i 
    f (Free (RightF (WriteInt i' x))) s _ = f x s i' 

我想一个可能性是表达使用iter :: Functor f => (f a -> a) -> Free f a -> a从运行仿函数,然后创建类似的组合函数

iter2 :: (Functor f, Functor g) 
     => (f a -> a) -> (g a -> a) -> Free (EitherF f g) a -> a 

但我没有时间去尝试一下。

+0

谢谢你,彼得。在你的帮助下,我明白了如何使用''(* - > *)''结合两个类型构造函数。 https://github.com/nushio3/practice/blob/master/operational/exe-src/test-06.hs 编写可组合的口译员也一样简单: https://github.com/nushio3/practice/ blob/master/operational/exe-src/test-07.hs 我们甚至可以以'OverlappingInstances'为代价编写两种以上的语言。 https://github.com/nushio3/practice/blob/master/operational/exe-src/test-08.hs – nushio