リストモナドを作ってみた

1,387 views

Published on

JavaScript の Array をモナドにしてみる遊び。明石Haskell #2 で発表したスライドです。
https://gist.github.com/4297448

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,387
On SlideShare
0
From Embeds
0
Number of Embeds
66
Actions
Shares
0
Downloads
4
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

リストモナドを作ってみた

  1. 1. 明石 Haskell #2
  2. 2. 自己紹介 @akanehara新大阪のWeb制作スタジオでプログラマーやってますFlashを長くやってました近頃は主に LAMP系Web / Androidアプリ / Java EEPythonをよく使います趣味でフライトシミュレータ作ったりScala経由でHaskellに関心を
  3. 3. 本日のお題
  4. 4. 明石焼きを たべにきた いかなご釘煮も買ってかえるよ!@a k a neh a ra
  5. 5. いやいやいや、
  6. 6. リストモナドを 作ってみた in 明石@a k a neh a ra
  7. 7. 突然ですがみんなだいすきリスト内包
  8. 8. みんなだいすきリスト内包単純に map, filter の別表現として[ x * x | x <- xs, odd x ]-- リスト xs のうち奇数の要素を抽出してさらに2乗生成部を複数もつことができ、組み合わせを展開できる[ (x, y) | x <- xs, y <- ys, x /= y]-- リスト xs, ys の要素のすべての組み合わせのうち-- 同じ要素でないものPythonのリスト内包も負けないくらい強力!(ステマ)[(x, y) for x in xs for y in ys if x != y]
  9. 9. リスト内包のひみつ[ (x, bar) | (x,y) <- foos, x < 2, bar <- bars, bar < y ] 等価foos >>= (x, y) -> guard (x < 2) >> bars >>= bar -> guard (bar < y) >> return (x, bar) http://en.wikibooks.org/wiki/Haskell/Syntactic_sugar
  10. 10. Haskellのリスト内包はリストモナドの糖衣構文
  11. 11. モナドこわい…>>=(´・ω・`)>>
  12. 12. モナドこわいなりに対応づけてみる(´・ω・`)
  13. 13. カラーばん リストないほうのひみつ[ (x, bar) | (x,y) <- foos, x < 2, bar <- bars, bar < y ] 等価foos >>= (x, y) -> guard (x < 2) >> bars >>= bar -> guard (bar < y) >> return (x, bar)
  14. 14. わからんなりに読み取れることfoos >>= (x, y) -> guard (x < 2) >> bars >>= bar -> guard (bar < y) >> return (x, bar)リストの >>=, return と guard 関数をまねることができれば(糖衣構文はさておき)リスト内包の力の源が手に入るっぽい
  15. 15. 作ってみよう!
  16. 16. 方針みんなだいすき(異論は認める)JavaScript実験なので手っ取り早く組み込みArrayを拡張var A = Array; // 長いからエイリアス(ひどい)演算子はオブジェクトのメソッドで語順をそれっぽくxs.mbind(function(x) { ...xs >>= x -> ...リスト内包の動作の再現をひとまずのゴールに
  17. 17. リストのreturnHaskell return :: Monad m => a -> m a return x = [x]JavaScriptA.mreturn = function(x) { return [x]; };// クラスメソッドA.prototype.mreturn = function(x) { return [x]; };// インスタンスメソッドreturn が予約語なので mreturn という名前で定義便宜上、クラスとオブジェクト両方に実装
  18. 18. リストの>>=Hakell (>>=) :: Monad m => m a -> (a -> m b) -> m b m >>= k = foldr ((++) . k) [] mぱっと見で何やってるかわかりづらい型の m を [ ] に置き換えて、さらに理解しやすく書きなおすとこう↓ (>>=) :: [a] -> (a -> [b]) -> [b] xs >>= f = concat $ map f xs左辺のリスト xs を 関数 f :: a -> [b] で map して得られるリストのリストを concat (連結)している
  19. 19. リストの>>=GHCi で試してみるPrelude> [1,2,3] >>= x -> [x, x*10][1, 10, 2, 20, 3, 30][1, 2, 3] -- 左辺のリストが[[1, 10], [2, 20], [3, 30]] -- mapされて…[1, 10, 2, 20, 3, 30] -- concat(連結)されてる
  20. 20. ところで >> は? さっき「リスト内包のひみつ」 で出てきた >> て何よfoos >>= (x, y) -> guard (x < 2) >> bars >>= bar -> guard (bar < y) >> return (x, bar)
  21. 21. ところで >> は?Haskell (>>) :: m a -> m b -> m b m >> k = m >>= _ -> k右辺が左辺値を使わない状況で >>= の代わりに使えるつまり、Prelude> [1..3] >>= _ -> [0,1][0,1,0,1,0,1]を、もっと短くこう書けるPrelude> [1..3] >> [0,1][0,1,0,1,0,1]
  22. 22. リストの>>=JavaScript// (>>=) :: Monad m => m a -> (a -> m b) -> m b// this >>= f = foldr ((++) . f) [] thisA.prototype.mbind = function(f) { return this.reduceRight( function(acc, x) { return f(x).concat(acc); }, [] );};
  23. 23. guard 関数定義guard :: MonadPlus m => Bool -> m ()guard True = return ()guard False = mzero _人人人人人人人人人_ > 突然の MonadPlus <  ̄Y^Y^Y^Y^Y^Y^Y^Y ̄
  24. 24. MonadPlus って何ぞ?class Monad m => MonadPlus m where mzero :: m a mplus :: m a -> m a -> m ainstance MonadPlus [] where mzero = [] mplus = (++)…加法?…単位元…?モナドをモノイドみたいに扱うの?何に使うんだろ(´・ω・`)今は気にしないでおこう(遅延理解)ともあれ、リストの mzero は空リストだということがわかる。
  25. 25. guard 関数やっぱり GHCi で試してみるPrelude> guard True >> [5][5]Prelude> guard False >> [5][]お、フィルタされた!同じ意味の別の式Prelude> [()] >>= _ -> [5][5]Prelude> [] >>= _ -> [5][][] が乗法の0みたいに機能してる…もしかして >>= って乗法(・∀・)?
  26. 26. guard 関数というわけで、まずは MonadPlus を実装JavaScript// mzero :: MonadPlus m => m a// mzero = []A.mzero = [];// mplus :: MonadPlus m => m a -> m a -> m a// mplus = (++)A.prototype.mplus = A.prototype.concat;
  27. 27. guard 関数JavaScript// guard :: MonadPlus m => Bool -> m ()// guard True = return ()// guard False = mzero// | 引数にMonadPlusを1つも取らないのでOOPの多態を頼れない// | 苦肉の策で型注釈代わりにクラスを与えるfunction guard(M, b) { if (b) { return M.mreturn(null); } else { return M.mzero; }}使うときは MonadPlus m が Array であることを引数で教える><guard(A, True) // guard True :: [()]guard(A, False) // guard False :: [()]
  28. 28. ここまでで>>= と return と guard が ったので
  29. 29. リスト内包ごっこHaskellPrelude> [(x, y) | x <- [1,2,3], y <- [1,2,3], x /= y ][(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]JavaScript[1,2,3].mbind(function(x) { return [1,2,3].mbind(function(y) { return guard(A, x != y).mbind(function() { return A.mreturn([x, y]); }); });});[[1,2],[1,3],[2,1],[2,3],[3,1],[3,2]] \(^o^)/ わーい!
  30. 30. あ。
  31. 31. モナド則
  32. 32. モナド則を満たしてないとちゃんと応用できない
  33. 33. 既存のモナドなので必要ないかとは思うけど、一応
  34. 34. モナド則1. return が >>= に関して左単位元2. return が >>= に関して右単位元3. 結合法則
  35. 35. Arrayの同値比較がないのでStackOverflowで拾ったこれ使いますfunction arrayEq(a, b) { return !(a < b || a > b); }
  36. 36. 1.return が >>= に関して左単位元Haskell(return x) >>= f == f xJavaScriptM.mreturn(x).mbind(f) == f(5)function f(x) { return [x, x*x, x*x*x]; }function g(x) { return [x*10, x*100, x*1000]; }arrayEq( A.mreturn(5).mbind(f), f(5) );> truearrayEq( A.mreturn(5).mbind(g), g(5) );> true
  37. 37. 2.return が >>= に関して右単位元Haskellm >>= return == mJavaScriptm.mbind(M.mreturn) == marrayEq( [1,2,3].mbind(A.mreturn), [1,2,3] ));> truearrayEq( [[1,2],[3,4]].mbind(A.mreturn), [[1,2],[3,4]] ));> true
  38. 38. 3.結合法則Haskell(m >>= f) >>= g==m >>= (x -> f x >>= g)JavaScript(m.mbind(f)).mbind(g)==m.mbind(function(x) { return f(x).mbind(g); })arrayEq( ([1,2,3].mbind(f)).mbind(g), [1,2,3].mbind(function(x) { return f(x).mbind(g); }));> true
  39. 39. 満たしてる! たぶん…(´・ω・`)ちゃんとした確かめかたは知らないです…
  40. 40. モナディック関数のあれやこれ// join :: m (m a) -> m a// join x = x >>= idfunction join(x) { return x.mbind(id); } // (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c// f (>=>) g = x -> f x >>= gfunction mcompose(f, g) { return function(x) { return f(x).mbind(g); };} // liftM :: (a1 -> r) -> m a1 -> m r// liftM f m1 = m1 >>= x1 -> return (f x1)function liftM(f) { return function(m1) { return m1.mbind(function(x1) { return m1.mreturn(f(x1)); }); };}mreturn と mbind しか使ってないので他のモナドでもちゃんと動くよ!
  41. 41. やってみた感想理解が加速した2冊めの本を読む感覚移植元の言語にあって移植先の言語にない機能の意義を実感定義を丁寧に読む動機づけになった楽しかった
  42. 42. ありがとうございました \(^o^)/ コードここにおいときますね↓https://gist.github.com/4297448

×