如何将Monad实例定义为具有多个值的类型?

时间:2017-04-28 15:51:33

标签: haskell

多个值我的意思是这样的:

data Foo a = Bar a | Baz a a

我想不出为>>=定义Baz的明确方法:

instance Monad Foo where
    Bar x   >>= f = f x -- Great, that works perfectly!
    Baz x y >>= f = ??? -- What the heck do I even put here?

1 个答案:

答案 0 :(得分:14)

也许:

frst (Bar a) = a
frst (Baz a a') = a

scnd (Bar a) = a
scnd (Baz a a') = a'

instance Monad Foo where
    return = Bar
    Bar x >>= f = f x
    Baz x y >>= f = Baz (frst (f x)) (scnd (f y))

此定义的灵感来自(>>=) (Bool ->)的定义。问我是否不清楚如何。

让我们检查一下法律。 “return是单位”法则非常简单:

  return x >>= f
= Bar x >>= f
= f x

  m >>= return
= case m of
      Bar x -> return x
      Baz x y -> Baz (frst (return x)) (scnd (return y))
= case m of
      Bar x -> Bar x
      Baz x y -> Baz x y
= m

我相信我已经说服了“(>>=)是联想”的法律,但我确信这个证据对其他人来说完全不可读......我鼓励你自己尝试证明,如果你遇到困难,请参考我的计算作为备忘单。

  m >>= (\v -> f v >>= g)
= case m of
      Bar x -> (\v -> f v >>= g) x
      Baz x y -> Baz (frst ((\v -> f v >>= g) x))
                     (scnd ((\v -> f v >>= g) y))
= case m of
      Bar x -> f x >>= g
      Baz x y -> Baz (frst (f x >>= g)) (scnd (f y >>= g))
= case m of
      Bar x -> case f x of
          Bar y -> g y
          Baz a b -> Baz (frst (g a)) (scnd (g b))
      Baz x y -> Baz (frst l) (scnd r) where
          l = case f x of
                  Bar a -> g a
                  Baz a b -> Baz (frst (g a)) (scnd (g b))
          r = case f y of
                  Bar a -> g a
                  Baz a b -> Baz (frst (g a)) (scnd (g b))
= case m of
      Bar x -> case f x of
          Bar y -> g y
          Baz a b -> Baz (frst (g a)) (scnd (g b))
      Baz x y -> Baz (frst (g (frst (f x))))
                     (scnd (g (scnd (f y))))
= case m of
      Bar a -> case f a of
          Bar x -> g x
          Baz x y -> Baz (frst (g x)) (scnd (g y))
      Baz a b -> case Baz (frst (f a)) (scnd (f b)) of
          Bar x -> g x
          Baz x y -> Baz (frst (g x)) (scnd (g y))
= case v of
      Bar x -> g x
      Baz x y -> Baz (frst (g x)) (scnd (g y))
  where v = case m of
                Bar a -> f a
                Baz a b -> Baz (frst (f a)) (scnd (f b))
= case m >>= f of
      Bar x -> g x
      Baz x y -> Baz (frst (g x)) (scnd (g y))
= (m >>= f) >>= g

编辑好吧,我决定写一篇简短的解释,说明这是如何受到(Bool ->)的启发,即使没人问。所以,回想一下:

instance Monad (e ->) where
    m >>= f = \e -> f (m e) e

现在我们要定义

data Pair a = Pair a a

并观察Bool -> aPair a非常相似:

to :: Pair a -> (Bool -> a)
to (Pair false true) = \bool -> case bool of
    False -> false
    True  -> true

from :: (Bool -> a) -> Pair a
from f = Pair (f False) (f True)

事实证明fromto是同构。换句话说:您可以将Bool -> a视为“双元素容器”。那么,如果我们尝试将(e ->)的{​​{1}}实例翻译成Monad类型,会发生什么?它当然应该是可能的,因为它们是同构的。实际上,让我们从同构开始:

Pair

现在我们可以“转动曲柄”:

instance Monad Pair where
    return x = from (return x)
    m >>= f = from (to m >>= to . f)

  return x
= from (return x)
= from (\e -> x)
= Pair ((\e -> x) False) ((\e -> x) True)
= Pair x x

因此,我们现在可以通过复制和粘贴上述计算的第一行和最后一行来重写实例而不依赖 m@(Pair false true) >>= f = from (to m >>= to . f) = from (\e -> (to . f) (to m e) e) = from (\e -> to (f (to m e)) e) = Pair (g False) (g True) where g = \e -> to (f (to m e)) e = Pair (to (f (to m False)) False) (to (f (to m True)) True) = Pair (case f (to m False) of Pair false true -> false) (case f (to m True ) of Pair false true -> true ) = Pair (case f false of Pair false true -> false) (case f true of Pair false true -> true )

(Bool ->)

希望你能够认识到这与我在frstPair (Pair false true) = false scndPair (Pair false true) = true instance Monad Pair where return x = Pair x x Pair false true >>= f = Pair (frstPair (f false)) (scndPair (f true)) 上面给出的(>>=)的定义有多么相似。

编辑2 另一个(不同的!)monad是可能的。从base:

中查看同构类型的行为
Foo

the docs for Product。写没有同构,它将是:

type Foo = Product Identity Maybe

从某种意义上说,我的原始提案会在您添加更多monadic操作时“扩展”结果数量 - 从instance Monad Foo where return x = Baz x x Bar x >>= f = Bar (frst (f x)) Baz x y >>= f = case f y of Bar a -> Bar (frst (f x)) Baz a b -> Baz (frst (f x)) b 开始Bar并将return不可撤销地转换为{绑定中的{1}} - 当您添加更多monadic操作时,此实例“收缩”可能的结果数量 - 从Bar中的Baz开始并转换Baz s return s不可挽回地在绑定中。如果你问我,这是一个有趣的设计选择!它还让我想知道Baz的另一个Bar实例是否可能(可能对所涉及的仿函数有不同的约束)。