言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

[haskell] How To Arrow 基本

-- なんか知らんうちに Arrow 流行ってるようなので試してみる。
-- http://www.cs.chalmers.se/~rjmh/afp-arrows.pdf によると、ポイントフ
-- リースタイルに IO を混ぜるのが Arrow だと考えればよいらしい。

import Control.Arrow

-- Arrow では高階型を使いまくるので慣れないとちょっと恐ろしく見える。
-- Arrow は Monad と同じくクラスだが、Haskell で言うクラスとは他でいう
-- インタフェースの事の事なので、色々な型に与えられた Arrow っぽい性質
-- の事を指す。Arrow ッぽい性質を持つものとしては、関数や Kleisli
-- (Monad をラップした物)等がある。自分ですぐ忘れてしまうので何度も書
-- くけど、関数の Arrow とは、関数が Arrow (is-a) なのではなく、関数の 
-- Arrow っぽさの事を言う。Arrow の演算子 >>> の両辺は同じ種類の Arrow 
-- じゃないといけない。一番簡単な関数の Arrow で説明する。関数の Arrow 
-- では >>> を関数合成 . の前後を入れ替えた物として使う。パイプみたい
-- とも言う。文字列を空白で切って単語を数えるにはこうする。arr は関数
-- から Arrow を生成するのに使う約束だが、関数をそのまま Arrow として
-- 使う時は無くても良い。

wc = arr words >>> arr length
-- *Main> wc "Hello, I am Takashi"
-- 4

-- 次に、ファイルを読んで文字列を数えるというのをやってみる。二つの IO 
-- 操作 readFile と print を使う。IO 操作から Arrow を作るには Kleisli 
-- 型を使う(読み方謎)。「Kleisli なんとか」のように書くとなんとかから 
-- モナドの Arrow (Kleisli の Arrow)が作成され、「runKleisli なんとか」
-- のように書くとなんとかからモナド(IO 操作) を取り出す事が出来る。
-- main 関数の結果として runKleisli を返すと >>> でつなげたモナドの
-- Arrow が実行される。

wcFile = Kleisli readFile >>> arr wc >>> Kleisli print
-- *Main> runKleisli wcFile "HowToArrow.hs"
-- 125

-- また、分配則があるので次のようにも書けるらしい。
wcFile2 = Kleisli readFile >>> arr (words >>> length) >>> Kleisli print
-- *Main> runKleisli wcFile "HowToArrow.hs"
-- 125

-- リストにリストモナドがあるように、ストリームファンクションという 
-- Arrow があって、リスト操作を直列に並べる事が出来る。

newtype SF a b = SF {runSF ::[a]->[b]}

instance Arrow SF where
    arr f = SF (map f)
    SF f >>> SF g = SF (f >>> g)

-- しょうもない例ですが、リストを逆順にして二倍する関数を定義。実際に
-- 実行するには runSF でリスト関数を取り出す必要がある。

doubleReverse = SF reverse >>> arr (* 2)
-- *Main> runSF doubleReverse [1..5]
-- [50,40,30,20,10]

-- ここまでは面倒なだけで Arrow の意味が無いような気がするけど、ここか
-- ら難しくなるのでアップする。