🔙 Back to Top

Sep 02 2013

Haskell, モナドと評価順序

Haskellにおける遅延評価でモナドが計算順序を定めることの意味が ようやくにして理解できた. つまり、いくら関数をつないでも渡した引数は必要になるまで評価 してくれない.それが入出力であっても.

import System.IO.Unsafe

getLn :: () -> String
getLn _ = unsafePerformIO getLine

int :: String -> Int
int = unsafePerformIO.readIO
getInt :: () -> Int
getInt () = int$getLn ()

double :: String -> Double
double = unsafePerformIO.readIO
getDouble :: () -> Double
getDouble () = double$getLn ()

unsafePerformIO :: IO a -> a である.なんて便利なんだ!プロコンでHaskellを使う時の為の テンプレートのつもりで上を書いた. getLn、getDouble は何かしら引数を渡さないといけない関数に するので毎回入力をとる. 初め

getDouble = double $ getLine

としていたから、getDoubleを複数回呼び出しても一行しか入力を 受け取ってくれなかった.

使ってみよう.

main =
    let x = getInt ()
        y = getInt () in
        print (x + y)

便利だ!

main =
    let x = getDouble ()
        a = getInt () in
        print (x ^ a)

たぶんパースエラーを起こす.私のGHCiは、(^)の演算にまず右側を 評価するらしいので、まず getInt を呼び出すらしいのだ. コードを見るとつい x を先に評価してるように思えてしまう.

遅延評価がデフォルトであることの意味がやっと分かった

じゃあどう書くかって言われて、こんなコードくらいしか書けないよ

main = do
    s <- getLine
    x <- readIO s :: IO Double
    t <- getLine
    a <- readIO t :: IO Int
    print (x^a)

do記法使っときながら (>>=) も使うなら

main = do
    x <- getLine >>= readIO :: IO Double
    a <- getLine >>= readIO :: IO Int
    b <- getLine >>= readIO :: IO Int
    print (x^(a+b))

なんかApplicativeとかに<$>だとかでまともに書けるのあった ようなのをなんとなく覚えてる.

ていうか、長いし、

(define-macro (getInt a)
    a <- getLine >>= readIO :: Int)

とか書きたくなる