制作 Monad class 的延续 monad 包装器实例

Making continuation monad wrapper instance of Monad class

我有类型 Foo,它是 Cont a a 的简单包装器。我想让 Foo 键入 Monad class 的一个实例。我试试这个:

import Control.Monad.Cont

newtype Foo a = Foo {unFoo :: Cont a a}

instance Monad Foo where
    return = Foo . return
    Foo inner >>= func = Foo (inner >>= newFunc)
        where newFunc x = (unFoo $ func x)

但是我得到了这个错误:

Couldn't match type `a' with `b'
  `a' is a rigid type variable bound by
      the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
      at Classes.hs:7:5
  `b' is a rigid type variable bound by
      the type signature for >>= :: Foo a -> (a -> Foo b) -> Foo b
      at Classes.hs:7:5
Expected type: ContT b Data.Functor.Identity.Identity a
  Actual type: Cont a a
In the first argument of `(>>=)', namely `inner'
In the first argument of `Foo', namely `(inner >>= newFunc)'
In the expression: Foo (inner >>= newFunc)

如何正确地为 Foo 添加 Monad 实例?

你无法将 Foo 变成 Monad

首先,我们要指出 Foo a(a -> a) -> a.

的一种复杂的写法
runFoo :: Foo a -> ((a -> a) -> a)
runFoo = runCont . unFoo

foo :: ((a -> a) -> a) -> Foo a
foo = Foo . cont

我们只有一种方式可以定义 (>>=) :: Foo a -> (a -> Foo b) -> Foo b。我们需要一个 a 来传递给箭头 a -> Foo b。我们唯一拥有 a 的是 Foo a,它等同于 (a -> a) -> a。如果我们可以提供类型为 a -> a 的函数,它会给我们一个 a,而只有 id 之一。所以我们如何获得 a 的唯一选择是通过 id.

instance Monad Foo where
    return = Foo . return
    ma >>= f = f (runFoo ma id)

这将违反 Monad 法则之一,m >>= return ≡ m。我们将编写一个存在于 Foo.

中的反例
counterExample :: Foo Int
counterExample = foo (\f -> if f 0 == 1 then 7 else 13)

反例在传递恒等函数 id 时得到 13,但在传递后继函数 (+1) 时得到 7

print $ runFoo counterExample id
print $ runFoo counterExample (+1)

13
7

根据 Monad 法则,counterExample' = counterExample >>= return 应与 counterExample 完全相同,但事实并非如此。 >>= 已经将 id 传递给函数,并且只 return 编辑了结果 13counterExample'counterExample 不一样。

let counterExample' = counterExample >>= return
print $ runFoo counterExample' id
print $ runFoo counterExample' (+1)

13
14

由于 >>= 只有一种可能的实现,而且它不正确,因此 Foo 没有正确的 Monad 实例。