我的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)
但我收到了这个错误:
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)
如何正确添加Monad
Foo
个实例?
答案 0 :(得分: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
我们只有一种方式可以定义(>>=) :: Foo a -> (a -> Foo b) -> Foo b
。我们需要a
传递给箭头a -> Foo b
。我们唯一拥有a
的{{1}}是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)
失败。我们将写一个住在13
的反例。
id
计数器示例在传递标识函数7
时产生(+1)
,但在传递后继函数print $ runFoo counterExample id
print $ runFoo counterExample (+1)
13
7
时仅产生Monad
。
counterExample' = counterExample >>= return
由于counterExample
法律,>>=
应该与id
完全相同,但它不可能。 return
已将13
传递给该函数,且仅counterExample'
编辑了结果counterExample
。 let counterExample' = counterExample >>= return
print $ runFoo counterExample' id
print $ runFoo counterExample' (+1)
13
14
并不像>>=
那样做。
Monad
由于Foo
只有一种可能的实现,并且它不正确,{{1}}没有正确的{{1}}实例。