Haskellの実験メモです。
アローの取っ掛かりをつかむため、次の記事のサンプルをアローなしで書き換えてみました。
今回の記事で書き換えを試みているのは、アローを使わずに済ませるためではなく、感覚をつかむために既知の概念と比較したいからです。
F#のパイプライン演算子や関数合成と似ているので、F#とも比較します。
準備
2つのテキストファイルを用意します。
hello, world
Consider this simple Haskell definition, of a function which counts the number of occurrences of a given word w in a string:
ファイルを読んで表示
アローの例を引用します。
import Control.Arrow
readFile' = Kleisli readFile
putStrLn' = Kleisli putStrLn
printFile = readFile' >>> putStrLn'
main = runKleisli printFile "test.txt"
hello, world
この例だとアローを持ち出さなくても>=>
でどうにかなります。
※ 実行結果は同じなので省略します。
import Control.Monad
printFile = readFile >=> putStrLn
main = printFile "test.txt"
>=>
はモナド則3の右辺に出て来るようなラムダ式を書き換えるのに使えます。
-
m >>= (\x -> f x >>= g)
→m >>= (f >=> g)
モナド則については次の記事を参照してください。
- モナド則がちょっと分かった? 2014.12.1
上の書き換えを逆に行えば>>=
で表現できます。
※ ポイントフリーであることがアローの目的の1つとして挙げられていますが、以下の書き換えにより引数が明示されポイントフリーではなくなっているため、コンセプトが台無しになっています。
printFile = \x -> readFile x >>= putStrLn
main = printFile "test.txt"
敢えてラムダ式にしなくても引数を左辺に移動しても同じです。
printFile x = readFile x >>= putStrLn
main = printFile "test.txt"
F♯
F#のパイプライン演算子|>
と似ています。
open System.IO
let printFile x = File.ReadAllText x |> printfn "%s"
printFile "test.txt"
パイプライン演算子 |>
を関数合成 >>
にすればポイントフリーにできます。Haskellで>=>
を使ったスタイルとほとんど同じ雰囲気です。
open System.IO
let printFile = File.ReadAllText >> printfn "%s"
printFile "test.txt"
指定した単語を数える
もう少し複雑な例を引用します。
import Control.Arrow
count w =
Kleisli readFile >>>
arr words >>>
arr (filter (==w)) >>>
arr (show . length) >>>
Kleisli putStrLn
main = runKleisli (count "a") "test2.txt"
3
同様にモナドで書き換えます。関数をMonad m => a -> m b
という型にするためreturn .
で合成しているのがミソです。
import Control.Monad
count w =
readFile >=>
return . words >=>
return . (filter (==w)) >=>
return . (show . length) >=>
putStrLn
main = (count "a") "test2.txt"
count w = \x ->
readFile x >>=
return . words >>=
return . (filter (==w)) >>=
return . (show . length) >>=
putStrLn
main = (count "a") "test2.txt"
count w x =
readFile x >>=
return . words >>=
return . (filter (==w)) >>=
return . (show . length) >>=
putStrLn
main = (count "a") "test2.txt"
F♯
F#も似たような感じで書けますが、words
に相当する部分がラムダ式になっていて少し冗長です。(もっと良い方法があるかもしれませんが)
open System.IO
let count w x =
File.ReadAllText x |>
(fun s -> s.Split ' ') |>
Array.filter ((=) w) |>
Array.length |>
printfn "%d"
(count "a") "test2.txt"
関数合成で x
をポイントフリーにします。
open System.IO
let count w =
File.ReadAllText >>
(fun s -> s.Split ' ') >>
Array.filter ((=) w) >>
Array.length >>
printfn "%d"
(count "a") "test2.txt"
感想
アローは>=>
が取っ掛かりになりそうだという感触がつかめました。
まさにそうやってアローの導入を試みた記事があります。とても分かりやすいのでお勧めです。
- @CyLomw: Arrow を理解する (まとめ) 2015.1.3
今回のような>=>
で書き換えられる程度のサンプルだと敢えてアローを使う必要もないのですが、もっと複雑になって来ればアローが真価を発揮するのでしょう。