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

ser*_*eyz 0 monads continuations haskell class instance

我的类型Foo是简单的包装Cont a a.我想使Foo类型成为类的实例Monad.我试试这个:

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)
Run Code Online (Sandbox Code Playgroud)

但我得到了这个错误:

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)
Run Code Online (Sandbox Code Playgroud)

如何正确添加Monad实例Foo

Cir*_*dec 5

你不能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
Run Code Online (Sandbox Code Playgroud)

我们只能定义一种方式(>>=) :: Foo a -> (a -> Foo b) -> Foo b.我们需要a传递给箭头a -> Foo b.我们唯一拥有as的东西是a Foo a,相当于(a -> a) -> a.它会给我们一个aif if我们可以提供一个类型的函数a -> a,它只有一个,id.所以我们唯一的选择a就是通过id.

instance Monad Foo where
    return = Foo . return
    ma >>= f = f (runFoo ma id)
Run Code Online (Sandbox Code Playgroud)

这将Monad违反其中一项法律m >>= return ? m.我们将写一个生活在其中的反例Foo.

counterExample :: Foo Int
counterExample = foo (\f -> if f 0 == 1 then 7 else 13)
Run Code Online (Sandbox Code Playgroud)

计数器示例在13传递身份函数时产生id,但仅7在传递后继函数时(+1).

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

13
7
Run Code Online (Sandbox Code Playgroud)

由于Monad规律,counterExample' = counterExample >>= return应该完全相同counterExample,但不可能.>>=已经传递id给函数并且只return编辑了结果13.counterExample'不做同样的事情counterExample.

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

13
14
Run Code Online (Sandbox Code Playgroud)

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