Monad むずいです。いつまでたってもいまいちちゃんと理解できないので、試しになんか書いてみることにしました。今回の題材は以下。
このページで使われているクローン羊さんの例を書いてみようと思います。…Vimで。
" Maybe Monad let Maybe = {} function! Maybe.return(m) return Just(a:m) endfunction " m(self) >>= f function! Maybe.bind(f) let Func = type(a:f) == type('') ? function(a:f) : a:f return self['bind_' . self.type](Func) endfunction function! Maybe.bind_Just(f) return a:f(self.m) endfunction function! Maybe.bind_Nothing(_) return Nothing() endfunction function! Maybe.show() return self['show_' . self.type](self) endfunction function! Maybe.show_Just(m) return 'Maybe "' . a:m.m.show() . '"' endfunction function! Maybe.show_Nothing(_) return 'Nothing' endfunction function! Maybe.fail(_) return Nothing() endfunction function! Just(m) return extend(copy(g:Maybe), {'type': 'Just', 'm': a:m}) endfunction function! Nothing() return extend(copy(g:Maybe), {'type': 'Nothing'}) endfunction " -- Sheep function! Sheep(name, mother, father) let sheep = {'name': a:name, 'mother': a:mother, 'father': a:father} function! sheep.show() " Helper function return self.name endfunction return sheep endfunction function! Name(sheep) return a:sheep.name endfunction function! Mother(sheep) return a:sheep.mother endfunction function! Father(sheep) return a:sheep.father endfunction
前半で Maybe モナドのような何かを定義しているつもりです。なお関数に渡すモナドは self になってます。
後半で羊さんを定義してます。Haskell なら型の要素を取り出す関数は自動で定義してくれるんですが、さすがに Vim がそんなことしてくれるわけないので全部自前です。
function! FmaternalGrandfather(sheep) return g:Maybe.return(a:sheep).bind('Mother').bind('Father') endfunction function! FfathersMaternalGrandmother(sheep) return g:Maybe.return(a:sheep).bind('Father').bind('Mother').bind('Mother') endfunction function! FmothersPaternalGrandfather(sheep) return g:Maybe.return(a:sheep).bind('Mother').bind('Father').bind('Father') endfunction let breedSheep = {} let breedSheep.adam = Sheep("Adam", Nothing(), Nothing()) let breedSheep.eve = Sheep("Eve", Nothing(), Nothing()) let breedSheep.uranus = Sheep("Uranus", Nothing(), Nothing()) let breedSheep.gaea = Sheep("Gaea", Nothing(), Nothing()) let breedSheep.kronos = Sheep("Kronos", Just(breedSheep.gaea), Just(breedSheep.uranus)) let breedSheep.holly = Sheep("Holly", Just(breedSheep.eve), Just(breedSheep.adam)) let breedSheep.roger = Sheep("Roger", Just(breedSheep.eve), Just(breedSheep.kronos)) let breedSheep.molly = Sheep("Molly", Just(breedSheep.holly), Just(breedSheep.roger)) let dolly = Sheep("Dolly", Just(breedSheep.molly), Nothing()) echo FmaternalGrandfather(dolly).show() echo FfathersMaternalGrandmother(dolly).show() echo FmothersPaternalGrandfather(dolly).show()
前半でお母さんのお父さんや、お父さんの母方の祖母や、お母さんの父方の祖父を探す関数を定義しています。
後半で世代関係のデータを作り、試しに探してみています。
以上のを合わせたのをここに貼っておきます。
で、これを実行すると以下のようになります。
Maybe "Roger" Nothing Maybe "Kronos"
おーちゃんと動いているようです。
比較
Vim:
function! FfathersMaternalGrandmother(sheep) return g:Maybe.return(a:sheep).bind('Father').bind('Mother').bind('Mother') endfunction
fathersMaternalGrandmother :: Sheep -> Maybe Sheep fathersMaternalGrandmother s = (return s) >>= father >>= mother >>= mother
形はだいぶ似てますね。処理自体も遅延評価じゃないところを除けば大体同じ…のはず、です。
今回の場合、モナドを使う事で、普通にやったら親を辿る度に親がいるかのチェックが必要なところをそのままバシバシ書けました。途中で親がいなくても返ってくるのは同じモナドなので同じように処理が続けられるってことなのかな。でもそれはMaybeモナドの話でモナド自体とはまた話が違うような…。うーーん、書いてみたけどやっぱりよくわかんない。
結論
とりあえず何でも Vim で書こうとするのはやめましょう。