モナドを使うと何が良いのか、モナドを使った場合と使わない場合を比較してみます。
関数プログラミングはデータに何度も関数を適用することにより希望する出力を得ます。データを引数として関数を適用し、その結果に関数を適用し・・・と関数プログラミングは幾重にもネストして関数を適用することになります。
- (>>=) は演算を連鎖させるためのものです。モナドを使わないで、Maybe 型(失敗するかも知れない演算)を引数とする関数を書くと Just と Nothing の場合分けをする必要があります。
addWorld :: Maybe String -> Maybe String addWorld m = case m of Nothing -> Nothing Just s -> Just (s++",world!") > addWorld (Just "Hello") -- > Just "Hello,world!" > addWorld Nothing -- > Nothing -- 連続して適用させた場合 > addWorld $ addWorld (Just "Hello") -- > Just "Hello,world!,world!" > addWorld $ addWorld Nothing -- > Nothing
- (>>=) を使うと Nothing の場合を考慮する必要がなくなります。
addWorld2 :: String -> Maybe String addWorld2 m = Just (m++",world!") > addWorld2 =<< Just "Hello" -- > Just "Hello,world!" > addWorld2 =<< Nothing -- > Nothing -- 連続して適用させた場合 > addWorld2 =<< addWorld2 =<< Just "Hello" -- > Just "Hello,world!,world!" > addWorld2 =<< addWorld2 =<< Nothing -- > Nothing
- return はモナドを作って注入します。
> let addWorld2 m = Just (m++",world!") > addWorld2 =<< return "Hello" -- > Just "Hello,world!" -- return は指定された型のモナドを作ります。 > (return "Hello" ) ::[String] -- > ["Hello"] > (return "Hello" ) ::Maybe String -- > Just "Hello" > (return "Hello" ) ::IO String -- > "Hello"
- Ruby で (>>=) を書いてみます。
class Maybe attr_reader :status, :dat attr_writer :dat def initialize(sts, dat) if sts then @status = :Just else @status = :Nothing end @dat = dat end def isNothing(); @status == :Nothing end def isJust(); @status == :Just end end # (>>=) がないとJustとNothingの場合分けをする必要があります。 def addWorld(obj) case obj.status when :Nothing obj when :Just obj.dat+=",world!" obj end end p addWorld(Maybe.new(false,"")) #=> #<Maybe:0xb77416cc @status=:Nothing, @dat=""> p addWorld(Maybe.new(true ,"hello")) #=> #<Maybe:0xb77416a4 @status=:Just, @dat="hello,world!"> # ここを簡潔に書けます。 addString = lambda{|obj| obj.dat +=",world!" } # Maybe 型の (>>=) と同じ動作をする関数 def bind(obj,func) case obj.status # Nothing >>= k = Nothing when :Nothing obj # (Just x) >>= k = k x when :Just obj.dat = func.call(obj) obj end end p bind(Maybe.new(false,""), addString) #=> #<Maybe:0xb7741564 @status=:Nothing, @dat=""> p bind(Maybe.new(true, "hello"), addString) #=> #<Maybe:0xb774153c @status=:Just, @dat="hello,world!">
おまけ:Maybe 型を便利に操作する関数は「Haskell 98 言語とライブラリ 改訂レポート / 18 Maybe ユーティリティ」にあります。