関数モナド
実は、通常の関数もモナドです。
関数モナドは一風変わったモナドです。
それがBindの型にも表れています。
後述のコードからBindの型だけ抜き出すと
val (>>=) : m: ('a -> 'b) -> f: ('b -> 'a -> 'c) -> x: 'a -> 'c
となるのですが、('a ->)をmとみなすと
('a -> 'b)はm 'b
('b -> 'a -> 'c)は'b -> m 'c
('a -> 'c)はm 'c
となっており、全部まとめると
m 'b -> ('b -> m 'c) -> m 'c
こうなり、いつものBindの型だということがわかります。
(いつもの'aが'b,'bが'cになってますが型変数名は付け替えても同じものです)
それではコードを見ていきます。
関数モナド
let (>>=) m f = fun x -> f (m x) x
let join m = m >>= id
type FuncBuilder() =
member __.Bind (m,f) = m >>= f
member __.Return x = fun _ -> x
let func = FuncBuilder()
let test1 = func{
let! a = ((+)1) //11
let! b = ((*)2) //20
return (a+b) //31
}
let test2 = func{
let! a = Some // a=Some [10]
return a
}
let test3 = func{
let! a = Array.length //3
let! b = Array.length >> ((+)1) //4
return (a+b)
}
let test4 = func{
let! a = join (+) //10+10 = 20
let! b = join (*) //10*10 = 100
return a + b //120
}
printfn "%A" <| test1 10
printfn "%A" <| test2 [10]
printfn "%A" <| test3 [|10;20;30|]
printfn "%A" <| test4 10
Bindの実装は短いですがちょっと難しいです。
丸覚えしてしまうのが良いかもしれません。
testでは、様々な1引数関数をlet!で受け取っています。
本家のhaskellでは、モナドを操作する関数が色々あるので
関数モナドはここで示した以上に便利なものです。
その一例としてjoin関数を定義して使ってみました。
returnとjoinがあればreturnとBindを使わなくても実はモナドが定義できます。
(>>=) m f = join (fmap f m)とBindが定義できるからです。
(ただしfmapが必要になります)
関数モナドにjoinを適用すると、二引数関数に同じ引数を2回渡して
1引数関数にするという動作になります。面白いですよね。
joinの型はm (m a) -> m aであり
関数モナドの場合は(a -> a -> b) -> a -> bとなります。