2013-07-30 46 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