私は最近、フリーFree
パッケージのモナドについて独学で学んでいますが、問題に遭遇しました。さまざまなライブラリにさまざまなフリーモナドを用意したいと考えています。基本的に、さまざまなコンテキストに対応する DSL を構築したいと考えていますが、それらを組み合わせることもできるようにしたいと考えています。例として:
{-# LANGUAGE DeriveFunctor #-}
module TestingFree where
import Control.Monad.Free
data BellsF x
= Ring x
| Chime x
deriving (Functor, Show)
type Bells = Free BellsF
data WhistlesF x
= PeaWhistle x
| SteamWhistle x
deriving (Functor, Show)
type Whistles = Free WhistlesF
ring :: Bells ()
ring = liftF $ Ring ()
chime :: Bells ()
chime = liftF $ Chime ()
peaWhistle :: Whistles ()
peaWhistle = liftF $ PeaWhistle ()
steamWhistle :: Whistles ()
steamWhistle = liftF $ SteamWhistle ()
playBells :: Bells r -> IO r
playBells (Pure r) = return r
playBells (Free (Ring x)) = putStrLn "RingRing!" >> playBells x
playBells (Free (Chime x)) = putStr "Ding-dong!" >> playBells x
playWhistles :: Whistles () -> IO ()
playWhistles (Pure _) = return ()
playWhistles (Free (PeaWhistle x)) = putStrLn "Preeeet!" >> playWhistles x
playWhistles (Free (SteamWhistle x)) = putStrLn "Choo-choo!" >> playWhistles x
今、私は両方のBellsAndWhistles
機能を組み合わせることができるタイプを作成できるようにしたいと考えています。Bells
Whistles
問題はモナドの結合にあるため、私が最初に考えたのはControl.Monad.Trans.Free
、迅速かつ簡単な解決策を求めてモジュールを調べることでした。残念ながら、まばらな例があり、私がやりたいことを示すものはありません。また、2 つ以上のフリー モナドをスタックしMonadFree
ても機能しないようですm -> f
。基本的に、次のようなコードを記述できる機能が必要です。
newtype BellsAndWhistles m a = BellsAndWhistles
{ unBellsAndWhistles :: ???
} deriving
( Functor
, Monad
-- Whatever else needed
)
noisy :: Monad m => BellsAndWhistles m ()
noisy = do
lift ring
lift peaWhistle
lift chime
lift steamWhistle
play :: BellsAndWhistles IO () -> IO ()
play bellsNwhistles = undefined
しかし、別々のモジュールに存在することができ、お互いの実装について知る必要がないような方法Bells
で。Whistles
アイデアは、それぞれが独自の DSL を実装し、必要に応じてそれらを「より大きな」DSL に結合する方法を持つ、さまざまなタスク用のスタンドアロン モジュールを作成できるということです。これを行う簡単な方法はありますか?
play*
おまけとして、既に記述されているさまざまな関数を活用して、それらを交換できるようになれば素晴らしいと思います。1 つの無料のインタープリターをデバッグ用に使用し、別のインタープリターを本番環境で使用できるようにしたいのですが、どの DSL を個別にデバッグするかを選択できると明らかに便利です。