LoginSignup
1
1

More than 5 years have passed since last update.

遅延評価を、IOアクションのリストで試す

Last updated at Posted at 2015-07-20

遅延評価は無限を取り扱える

Haskellでは遅延評価という仕組みがある。この仕組みの中では、関数は引数にとるデータについて必要な分だけを使うことになる。

例えば、

take 3 [0..] -- [0,1,2]

とすれば、0から無限に続く正の整数というまとまりから、先頭の3つの要素を取り出すことができる。これはとても便利な仕組みだ。なぜなら、 まとまりそのものと、それをいくつ取り出すかという条件を独立させることができるからだ。これにより、プログラマは対象となるまとまりそのものと、その停止条件を別個に設計できるようになる。

無限の例
filter even [0..] -- 偶数
filter odd [0..] -- 奇数
fibs -- フィボナッチ数列

fibs = 1 : 1: zipWith (+) fibs (tail fibs)

なお、先の例をJavaScriptで書くとこのようになるだろう。

function take(n) {
    var arr = [];
    var i;
    for (i = 0; i < n; i++) {
        arr.push(i);
    }
    return arr;
}

後者は、n個の整数といった有限のまとまりしか扱っていないことがわかる。これを切り離して無限にしてしまったら、プログラムが停止してしまう。モジュール性の観点から言えば、まとまりそのものと、それをいくつ取り出すかという条件を分けて表現できた方が嬉しいことが多い(そんなことを前に書いた:モジュール化について考えてみたときのまとめ)

遅延評価とIO

そんななか、何か他に良い題材はないかと考えていたところ、いつも参考にさせていただいてるブログの遅延評価とIOという記事にとても興味を引くことが書いてあった。

つまり IO に関わる関数さえも、遅延評価というノリのおかげで、完全に独立な部品となるのである。

なるほど、IOに関わる関数さえもなんらかのまとまりとして扱うことができる。あとは独立して停止条件を設計し、つなぎ合わせれば無駄な処理なく必要な分だけ実行できるようになるということだ。WebアプリケーションのようにIOモナドの実装が多くなりがちなケースでは特に、この実装方針を応用して、よりリーダブルなコードにしたい!と思った。
具体的にはHTTPから受け取るデータとか、ロードするデータ(ファイルまたはデータベース)に対する操作を、なんらかのまとまりとして捉える、ということになるだろう。

サンプル

とても残念だが、実践的なアイデアは浮かばなかった。代わりに、次のようなまとまりと停止条件を考えてみた。

モジュール サンプル仕様
まとまり ランダムな値を生成し、標準出力に表示するIOアクションが無限に続くリスト
停止条件 生成された値が、実行時に引数として渡す任意の整数にマッチするまで

次のようなプログラムになった。

lazyList.hs
import System.Random
import System.Environment
import Control.Monad

-- 0から30までのInt型の値をランダムに選び、標準出力に表示したあとに返す
randInt :: IO Int
randInt = do
    gen <- newStdGen
    let ret = fst $ randomR (0, 30) gen
    putStrLn $ show ret
    return ret

-- IO Int のリストをとり、指定した数字にマッチするまでIOアクションを実行する
searchNumber :: Int -> [IO Int] -> IO ()
searchNumber _ [] = putStrLn "Nothing"
searchNumber x (im:ims) = do
    y <- im -- ここでIO アクションが実行される
    unless (x == y) $ searchNumber x ims

main :: IO ()
main = do
    args <- getArgs
    searchNumber (read $ head args) (repeat randInt)
実行結果
$ runhaskell lazyList.hs 10
6
18
24
10

無限に続くIOアクションのリストを引数にとるものの、引数に入れた数字とマッチすれば即座に停止していることがわかる。
また、randIntsearchNumberそれそれが独立しているので、別の関数やまとまりとつなぎ合わせることが可能になっている。

終わりに

最近、Node.jsも並行して学習しているのだが、ここでもストリームという概念が登場していて、利用する目的が似ている。まとまりとそれをどう取り扱うか、ということについてきちんと知識と勘所を正しく身に付けることが、次世代のプログラミングスキルとしてとっても重要な気がしている。

1
1
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
1
1