LoginSignup
5
5

More than 5 years have passed since last update.

アローの取っ掛かり

Last updated at Posted at 2015-12-07

Haskellの実験メモです。

アローの取っ掛かりをつかむため、次の記事のサンプルをアローなしで書き換えてみました。

今回の記事で書き換えを試みているのは、アローを使わずに済ませるためではなく、感覚をつかむために既知の概念と比較したいからです。

F#のパイプライン演算子や関数合成と似ているので、F#とも比較します。

準備

2つのテキストファイルを用意します。

test.txt
hello, world
test2.txt
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

この例だとアローを持ち出さなくても>=>でどうにかなります。

※ 実行結果は同じなので省略します。

モナド1
import Control.Monad

printFile = readFile >=> putStrLn

main = printFile "test.txt"

>=>はモナド則3の右辺に出て来るようなラムダ式を書き換えるのに使えます。

  • m >>= (\x -> f x >>= g)m >>= (f >=> g)

モナド則については次の記事を参照してください。

上の書き換えを逆に行えば>>=で表現できます。

※ ポイントフリーであることがアローの目的の1つとして挙げられていますが、以下の書き換えにより引数が明示されポイントフリーではなくなっているため、コンセプトが台無しになっています。

モナド2
printFile = \x -> readFile x >>= putStrLn

main = printFile "test.txt"

敢えてラムダ式にしなくても引数を左辺に移動しても同じです。

モナド3
printFile x = readFile x >>= putStrLn

main = printFile "test.txt"

F♯

F#のパイプライン演算子|>と似ています。

F#
open System.IO

let printFile x = File.ReadAllText x |> printfn "%s"

printFile "test.txt"

パイプライン演算子 |> を関数合成 >> にすればポイントフリーにできます。Haskellで>=>を使ったスタイルとほとんど同じ雰囲気です。

F#
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 .で合成しているのがミソです。

モナド1
import Control.Monad

count w =
    readFile >=>
    return . words >=>
    return . (filter (==w)) >=>
    return . (show . length) >=>
    putStrLn

main = (count "a") "test2.txt"
モナド2
count w = \x ->
    readFile x >>=
    return . words >>=
    return . (filter (==w)) >>=
    return . (show . length) >>=
    putStrLn

main = (count "a") "test2.txt"
モナド3
count w x =
    readFile x >>=
    return . words >>=
    return . (filter (==w)) >>=
    return . (show . length) >>=
    putStrLn

main = (count "a") "test2.txt"

F♯

F#も似たような感じで書けますが、wordsに相当する部分がラムダ式になっていて少し冗長です。(もっと良い方法があるかもしれませんが)

F#
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 をポイントフリーにします。

F#
open System.IO

let count w =
    File.ReadAllText >>
    (fun s -> s.Split ' ') >>
    Array.filter ((=) w) >>
    Array.length >>
    printfn "%d"

(count "a") "test2.txt"

感想

アローは>=>が取っ掛かりになりそうだという感触がつかめました。

まさにそうやってアローの導入を試みた記事があります。とても分かりやすいのでお勧めです。

今回のような>=>で書き換えられる程度のサンプルだと敢えてアローを使う必要もないのですが、もっと複雑になって来ればアローが真価を発揮するのでしょう。

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5