ParserT モナドの実体は、唯一のフィールドに無名関数を入れる代数的データ型だ。newtype 宣言で次のように定義されている。 newtype ParsecT s u m a ....= ParsecT {unParser :: forall b . ................ State s u ..............-> (a -> State s u -> ParseError -> m b) -- consumed ok ..............-> (ParseError -> m b).................. -- consumed err ..............-> (a -> State s u -> ParseError -> m b) -- empty ok ..............-> (ParseError -> m b).................. -- empty err ..............-> m b ............ } このデータ構造には2つの特徴がある。一つはフィールドのデータが無名関数であることだが、もう一つは、引数の型が関数であるということだ。この意味がなかなか分かりづらかったので、同様のデータ型を作ってどういう動作をするのか試してみた。 まず、フィールドのデータが無名関数で、その引数が関数型であるような代数的データ型 Foo a を作ってみた。Foo はフィールド名 unpack のフィールドに無名関数を収める。無名関数の引数は第1引数が数値で第2引数と第3引数が関数だ。戻り値は数値になる。 Prelude> data Foo a = Foo {unpack :: a -> (a -> a) -> (a -> a) -> a} そこで次のような Foo 型のデータ foo を作った。foo の unpack フィールドには \x f g -> if odd x then f x else g x という無名関数を入れた。この関数は x が odd なら f x を戻り値として返し、そうでない場合は g x を戻り値として返す。 Prelude> foo = Foo $ \x f g -> if odd x then f x else g x これに実引数を渡して動作を確認した。 Prelude> unpack foo 3 (*10) (^2) 30 Prelude> unpack foo 4 (*10) (^2) 16 これを見ると Foo の unpack フィールドの関数が x の値で戻り値を切り替えるという抽象的な動作をする関数であることが分かる。この x の値次第で戻り値を切り替えるという性質を利用すると再帰関数も定義できる。これを検証するために、x の値が 0 のときと、それ以外の場合に振り分ける Foo 型のデータ baz を定義する。 Prelude> baz = Foo $ \x f g -> if x == 0 then f x else g x この baz が x が 0 のときは f x を実行し、それ以外のときは g x を実行するという抽象的な性質に注目すると、次のように再帰関数も記述することができる。 Prelude> fact x = unpack baz x (\_ -> 1) (\s -> s * fact (s-1)) Prelude> fact 5 120 Foo のフィールドの関数は通常の関数だったが、モナド値を返すモナド関数にすることができる。そこで、Foo 型に似ているが Maybe a 型を返す関数を収めるデータ型 Bar を定義してみた。 Prelude> data Bar a = Bar {unpack :: a -> (a -> Maybe a) -> (a -> Maybe a) -> Maybe a} foo と同じような Bar 型のデータ bar を次のように定義する。無名関数の本体のコードは foo の場合と全く同じだがこれは関数の抽象的な性質を考えると驚くべきことではない。 Prelude> bar = Bar $ \x f g -> if odd x then f x else g x bar に実引数を与えた結果を次に示すが、関数の引数の部分のプログラムをモナドで記述することができる。 Prelude> unpack bar 3 (return) (\_ -> Nothing) Just 3 Prelude> unpack bar 4 (return) (\_ -> Nothing) Nothing 無名関数をフィールドデータとするデータ型や、引数が関数型の関数を自前で作って動作させてみたら、この形式のデータの扱いが少し分かってきた。Text.Parsec.Prim のパーサのプログラムの意味もなんとなく掴めた気がする。
by tnomura9
| 2019-07-17 21: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 言語 命題論理 以前の記事
最新のトラックバック
最新のコメント
ファン
記事ランキング
ブログジャンル
画像一覧
|
ファン申請 |
||