モナドって何だろう

モナドを使うと何が良いのか、モナドを使った場合と使わない場合を比較してみます。
関数プログラミングはデータに何度も関数を適用することにより希望する出力を得ます。データを引数として関数を適用し、その結果に関数を適用し・・・と関数プログラミングは幾重にもネストして関数を適用することになります。

  • (>>=) は演算を連鎖させるためのものです。モナドを使わないで、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 ユーティリティ」にあります。