top page > computer > haskell > web_lecture > for_programmer > do_notation.html
更新日:
文責: 重城良国

構文: do記法

(工事中 60%)

型クラスMonadは特別

文脈を対象とした型クラスは数多くあるがHaskellにおいて型クラスMonadは特別な扱いを受けている。型クラスMonadのインスタンスだけに適用される構文糖がある。あとで学ぶがHaskellでは状態変化や外部とのやりとりをIOという型によって扱う。これらの入出力は「機械をつなぐ」という考えかたで行われる。「つなぐ」インターフェースとして型クラスMonadのクラス関数が使われている。それが特別な構文糖が用意されている理由であり、その構文糖をdo記法と呼ぶ。

メモリつき電卓の例

do_notation.hs

前回のCalcのコピペでいいと思うがここに再掲する(ghc-7.10以降であればimport Control.Applicativeは不要なのであとで削除する)。

{-# LANGUAGE TupleSections #-}

import Control.Applicative

newtype Calc a = Calc { runCalc :: Int -> (a, Int) }

instance Functor Calc where
fmap = (=<<) . (return .)

instance Applicative Calc where
pure = return
mf <*> =
mf >>= \f ->
mx >>= \x ->
return $ f x

instance Monad Calc where
return = Calc . (,)
m >>= f = Calc $ \s ->
let (x, s') = runCalc m s in runCalc (f x) s'

mplus :: Int -> Calc ()
mplus x = Calc $ (() ,) . (+ x)

mrecall :: Calc Int
mrecall = Calc $ \s -> (s, s)

do記法を使わないと

メモリつき電卓で(3 * 4 + 2 * 5) * 7を計算することを考える。do記法を使わないと

calc :: Calc Int
calc =
return (3 * 4) >>=
mplus >>
return (2 * 5) >>=
mplus >>
mrecall >>=
return . (* 7)

のようになる。

仮引数の明示

do記法への書き換えのために仮引数を明示する形に書き換える。

calc =
return (3 * 4) >>= \x ->
mplus x >>
return (2 * 5) >>= \y ->
mplus y >>
mrecall >>= \z ->
return (z * 7)

do記法

これをdo記法を使って書き直すと

calc = do
x <- return (3 * 4)
mplus x
y <- return (2 * 5)
mplus y
z <- mrecall
return (z * 7)

となる。1行目にdoをつけてその後は「exp >>= \v ->」を「v <- exp」に「exp >>」を「exp」に変換する。do以降のそれぞれの行はインデントをそろえる必要がある。

do記法のlet構文

「var <- return val」はモナド則を守った正しいモナドでは値valで変数varを束縛しているのと同じことになる。さらに甘くする。

calc = do
let x = 3 * 4
mplus x
let y = 2 * 5
mplus y
z <- mrecall
return (z * 7)

「var <- return val」を「let var = val」に置きかえた。

おまけ: もっとシンプルに

モナドを学ぶための例だったのであえて冗長な書きかたをしてきた。もちろん(3 * 4 + 2 * 5) * 7を計算するにはそのまま

(3 * 4 + 2 * 5) * 7

とすればいいわけだがとりあえずCalcモナドのまま簡略にしていこう。まずわざわざ変数xやyに整数を束縛して使う必要はない。また変数zも消すことができる。

calc = do
mplus $ 3 * 4
mplus $ 2 * 5
mrecall >>= return . (* 7)

最後のところでdo記法を使わない記法が復活している。このようにdo記法と(>>=)を明示する書きかたは併用することができる。ここでfmap f = (>>= return . f)を考えると

calc = do
mplus $ 3 * 4
mplus $ 2 * 5
fmap (* 7) mrecall

となる。アプリカティブスタイルのところで導入した関数fmapの別名を使うと

calc = do
mplus $ 3 * 4
mplus $ 2 * 5
(* 7) <$> mrecall

とできる。「3 * 4の結果をメモリに足し、2 * 5の結果をメモリに足し、mrecallによって返ってきた結果を7倍する」ということだ。

{};を明記する書きかた

(この項目はあとで書く)

まとめ

モナド関数による計算の連鎖はdo記法で書きかえることができる。先頭にdoをつけて、その後の行はインデントをそろえる。そして以下のような書きかえを行う。

do記法と(>>=)を明記する書きかたとは併用することができる。アプリカティブスタイルとの併用も可能だ。インデントルールを使わずに{};を明記する書きかたもある。これらのいくつかの書きかたを適切に選択することで、簡潔さや、わかりやすさを調整することができる。

「型クラス: Monad」へもどる 「リストモナド」へ

正当なCSSです! HTML5 Powered with CSS3 / styling, and Semantics