次のように、簡単なユーザ定義型のデータをモナドにするのはひどく簡単だった。
Prelude> :set +m Prelude> newtype M a = M a deriving Show Prelude> instance Monad M where Prelude| (M x) >>= f = f x Prelude| return x = M x Prelude| Prelude> M 2 >>= \x -> M (x * 2) M 4 そこで、今度は State モナドをターゲットにして自前のモナドの作り方を調べようとしたが、当てが外れた。 Prelude> :m Control.Monad.State Prelude Control.Monad.State> :t State Not in scope: data constructor `State' Perhaps you meant `StateT' (imported from Control.Monad.State) Prelude Control.Monad.State> :info State type State s = StateT s Data.Functor.Identity.Identity -- Defined in `Control.Monad.Trans.State.Lazy' どうも State モナドは StateT モナドに統合されてしまっているようだ。State モナドの性質を知らずに StateT モナドを調べるのはつらいものがあるので、いっそのこと MyState モナドを作ってみようと思いついた。 古い State 型の定義は次のようになっている。 newtype State s a = State { runState :: (s -> (a,s)) } State 型のパラメータは (s -> (a,s)) 型の関数で runState という名つきフィールドになっている。したがってこの関数はアクセサ runState で簡単に取り出すことができる。まず、これを真似して MyState 型を定義してみた。 Prelude> newtype MyState s a = MyState { runMyState :: s -> (a,s) } 次のように MyState 型のデータ st1 を定義してみると、確かに runMyState でコンテナの関数が取り出せるのが分かる。 Prelude> let st1 = MyState (\x -> ("hello", x*x)) Prelude| Prelude> runMyState st1 2 ("hello",4) つぎに、この MyState 型を『 all about monads 』の記事を真似して Monad クラスのインスタンスにしてみる。 Prelude> instance Monad (MyState s) where Prelude| return a = MyState $ \s -> (a,s) Prelude| (MyState x) >>= f = MyState $ \s -> let (v,s') = x s in runMyState (f v) s' Prelude| これでめでたく MyState 型がモナドになったので return 関数を試してみる。 Prelude> runMyState (return 2 >>= \a -> MyState (\s -> (a,s))) 3 (2,3) runMyState 関数の戻値のペアの fst 要素に return の値 2 が、snd 要素に状態の値の 3 が収められている。return 関数はうまくいったようだ。次に >>= (bind 演算子) を試すために次のような2つの MyState モナドの Kleisli 射を定義する。Kleisli 射とは引数が一つで戻値が MyState s a 型の関数だ。 Prelude> let Prelude| kleisli1 a = MyState (\s -> (a, s*s)) Prelude| kleisli2 a = MyState (\s -> (a*a, s)) Prelude| return 関数とこの2つの Kleisli 射を >>= で合成してためしてみると次のように上手く働くことが分かる。 Prelude> runMyState (return 2 >>= kleisli1) 3 (2,9) Prelude> runMyState (return 2 >>= kleisli2) 3 (4,3) Prelude> runMyState (return 2 >>= kleisli1 >>= kleisli2) 3 (4,9) もちろん、do 記法も使える。 Prelude> runMyState (do x <- return 2; kleisli1 x) 3 (2,9) このように自前の MyState 型をモナドにするのは意外に簡単なことが分かる。 もっとも上の MyState モナドには get 関数と put 関数がない。この2つの関数は MonadState タイプクラスの多相関数だが、ここでは一般の関数として定義してみる。 Prelude> let Prelude| get = MyState $ \s -> (s,s) Prelude| put s = MyState $ \_ -> ((),s) Prelude| それでは、MyState モナドの tick 関数を作ってみよう。 Prelude> let Prelude| tick = do Prelude| x <- get Prelude| put (x+1) Prelude| return x Prelude| テストしてみる。 Prelude> runMyState tick 0 (0,1) Prelude> runMyState (tick >> tick >> tick) 0 (2,3) どうやら MyState モナドでも tick 関数はうまくはたらくようだ。 自前の状態モナドを作ってみたが、プロトタイプは意外に簡単にできることがわかった。自前のモナドを作るときの取っ掛かりができたような気がする。
by tnomura9
| 2013-05-03 12:38
| Haskell
|
Comments(0)
|
カテゴリ
新型コロナウイルス 主インデックス Haskell 記事リスト 圏論記事リスト 考えるということのリスト 考えるということ ラッセルのパラドックス Haskell Prelude Ocaml ボーカロイド 圏論 jQuery デモ HTML Python ツールボックス XAMPP Ruby ubuntu WordPress 脳の話 話のネタ リンク 幸福論 キリスト教 心の話 メモ 電子カルテ Dojo JavaScript C# NetWalker ed と sed HTML Raspberry Pi C 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||