25. Monad instance定義
• IOの場合はすでにMonadなので不要
• newtypeでMonadをwrapした場合もderivingで導出出来
る
instance Monad MyMonad where
return a = MyMonad $ return a
m >>= k = MyMonad $ do
a <- unwrap m
unwrap $ k a
29. “一般的”なMonad, State
• 一般的なMonadの簡単な例としてはStateが適当
newtype State s a = State { runState :: s -> (a, s) }
instance Monad (State s) where
return a = State $ s -> (a, s)
m >>= k = State $ s -> let
(a, s') = runState m s
in runState (k a) s'
30. State Monad
• 固有actions
• get :: State s s
• put :: s -> State s ()
• run関数
• runState :: State s a -> (s -> (a, s))
44. DSLをHaskell上で実装する
• 基本的にはMonadのサブクラスでいい
class Monad m => MonadMyDSL m where
getLine :: m Text
echo :: Text -> m ()
• Monad InstanceはIOで問題ない
instance MonadMyDSL IO where …
45. DSL上のロジックの記述
• 以下の様に多相的に記述する
• foo :: MonadMyDSL m => Text -> m (Text, Int)
• bar :: MonadMyDSL m => Int -> Text -> m ()
• DSL m の仮定下においては、DSL m のprimitivesしか使
えない
• Haskellの型制約を利用したDSLの完結
46. 多相化に伴う
パフォーマンス問題
• GHC PRAGMAの一つ、SPECIALIZEで対処する
• foo :: MonadMyDSL m => m Text
• {-# SPECIALIZE foo :: IO Text #-}
• 実質IOで動作する
51. TransformerによるDSLネスト
• newtype BaseDSL a = BaseDSL { unBDSL :: IO a }
• newtype EmbDSL' m a = EmbDSL' { unEDSL’ :: m a }
• type EmbDSL a = EmbDSL’ BaseDSL a
• instance MonadTrans EmbDSL’ where …
!
• runEmbDSL :: EmbDSL a -> BaseDSL a
• runEmbDSL = unEmbDSL’
52. IO/ST/STM/Identityを
経由したDSLネスト
• IO/ST/STM/IdentityはMonad Stackの底に位置し得る
• newtype BaseDSL a = BaseDSL { unBDSL :: IO a }
• newtype EmbDSL a = EmbDSL { unEDSL :: IO a }
!
• runEmbDSL :: EmbDSL a -> BaseDSL a
• runEmbDSL = liftIO . unEDSL
58. DSLの合成
• DSLはMonadのサブクラスとして定義してある
• 複数のDSLを定義しておくことにより、DSLの合成は型
クラスによるad-hock多相で容易に可能
• class Monad m => DSL1 m where …
• class Monad m => DSL2 m where …
• baz :: (DSL1 m, DSL2 m) => m ()
• qux :: (DSL1 m, DSL2 m) => m ()