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
?
你不能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
.我们唯一拥有a
s的东西是a Foo a
,相当于(a -> a) -> a
.它会给我们一个a
if 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
.