必要なほとんどすべてのタイプを表すことができます。しかし、モナド操作は型ごとに異なる方法で実装されるため、一度記述してすべてのインスタンスで機能させることはできません。>>=
ただし、型クラスのインスタンスの証拠に依存する汎用関数を作成できます。ここでは、「バインド」定義を含み、「戻り」定義を含むタプルであると考えてください。e
fst e
snd e
bind = λe. fst e -- after applying evidence, bind looks like λmf. ___
return = λe. snd e -- after applying evidence, return looks like λx. ___
fst = λt. t true
snd = λt. t false
-- join x = x >>= id
join = λex. bind e x (λz. z)
-- liftM f m1 = do { x1 <- m1; return (f x1) }
-- m1 >>= \x1 -> return (f x1)
liftM = λefm. bind e m (λx. return e (f x))
次に、Monad のすべてのインスタンスに対して「証拠タプル」を定義する必要があります。bind
と を定義した方法に注意してくださいreturn
: これらは、定義した他の「一般的な」Monad メソッドと同じように機能します: 最初にモナド性の証拠を与えなければならず、次に期待どおりに機能します。
Maybe
2 つの入力を受け取る関数として表すことができます。1 つ目は の場合に実行する関数Just x
であり、2 つ目はそれが Nothing の場合に置き換える値です。
just = λxfz. f x
nothing = λfz. z
-- bind and return for maybes
bindMaybe = λmf. m f nothing
returnMaybe = just
maybeMonadEvidence = tuple bindMaybe returnMaybe
リストも同様で、折り畳み関数としてリストを表します。したがって、リストは「cons」と「empty」の 2 つの入力を受け取る関数です。次にfoldr myCons myEmpty
、リストで実行します。
nil = λcz. z
cons = λhtcz. c h (t c z)
bindList = λmf. concat (map f m)
returnList = λx. cons x nil
listMonadEvidence = tuple bindList returnList
-- concat = foldr (++) []
concat = λl. l append nil
-- append xs ys = foldr (:) ys xs
append = λxy. x cons y
-- map f = foldr ((:) . f) []
map = λfl. l (λx. cons (f x)) nil
Either
も簡単です。いずれかの型を 2 つの関数を取る関数として表します。1 つは の場合にLeft
適用し、もう 1 つは の場合に適用しますRight
。
left = λlfg. f l
right = λrfg. g r
-- Left l >>= f = Left l
-- Right r >>= f = f r
bindEither = λmf. m left f
returnEither = right
eitherMonadEvidence = tuple bindEither returnEither
関数自体 (a ->)
がモナドを形成することを忘れないでください。そして、ラムダ計算のすべては関数です...だから...あまり難しく考えないでください。;) Control.Monad.Instancesのソースから直接インスピレーションを得ています
-- f >>= k = \ r -> k (f r) r
bindFunc = λfkr. k (f r) r
-- return = const
returnFunc = λxy. x
funcMonadEvidence = tuple bindFunc returnFunc