制作 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
编辑了结果 13
。 counterExample'
与 counterExample
不一样。
let counterExample' = counterExample >>= return
print $ runFoo counterExample' id
print $ runFoo counterExample' (+1)
13
14
由于 >>=
只有一种可能的实现,而且它不正确,因此 Foo
没有正确的 Monad
实例。
我有类型 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
编辑了结果 13
。 counterExample'
与 counterExample
不一样。
let counterExample' = counterExample >>= return
print $ runFoo counterExample' id
print $ runFoo counterExample' (+1)
13
14
由于 >>=
只有一种可能的实现,而且它不正确,因此 Foo
没有正确的 Monad
实例。