我正在尝试运行此
newtype Test a = Test (Int, a)
instance Monad Test where
Test (_, []) >>= k =
k []
Test (_, a) >>= k =
k a
return a =
Test (0, a)
我收到错误:
Couldn't match expected type `a' with actual type `[t0]'
`a' is a rigid type variable bound by
the type signature for >>= :: Test a -> (a -> Test b) -> Test b
at C:\Users\david.phillips\Documents\code\test.hs:4:5
In the pattern: []
In the pattern: (_, [])
In the pattern: Test (_, [])
当我尝试使用case语句而不是2个版本的>> =时,我收到类似的错误。
我对haskell很新,但不明白为什么这不起作用。
编辑:对不起,这是一个糟糕的例子。假设>> =的第一个定义给出了不同的输出。
答案 0 :(得分:8)
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
Monad
个实例通过类型变量a
进行参数化。你本质上声称它不是,因为在该变量的位置,你在空列表构造函数[]
上匹配了模式,意思是“a是某个列表”。例如的类型。 bind可以通过明确的量化来编写:
(>>=) :: forall a. m a -> (a -> m b) -> m b
您的定义不可能适用于所有a
。
答案 1 :(得分:3)
作为Sarah答案的补充,您实际上可以省略特定的模式匹配,
Test (_, []) >>= f = f []
与
相同 Test (_, a) >>= f = f a
所以你真的可以把它写成
Test (_, a) >>= f = f a
return a = (0, a)
现在请记住,除了拥有正确的类型之外,我们应该有那个
m >>= return = m
return a >>= f = f a
(a >>= b) >>= c = a >>= (\a' -> b a' >>= c)
这让我很担心,因为
m = Test (1, 'c')
m >>= return === return 'c' === (0, 'c') /== m
所以return
不再是一个身份,而你正在打破第一个monad法则。解决这个问题意味着return
必须保留元组的第一个元素,这是有问题的,因为我们实际上并没有告诉它。
天真地,让我们破坏我们的函数返回的第一个元组元素。
Test (a, b) >>= f = Test (a, snd (f b))
现在
Test (1, 'c') >>= return == Test (1, snd (return 'c')) == Test (1, 'c')
但我们仍然遇到麻烦,
let f a = Test (1, a)
return a >>= f == Test (0, snd (f a)) == Test (0, a) /== f a
所以这里的诀窍是做状态monad做的事情
newtype Test a = Test{ unTest :: Int -> (Int, a) }
instance Monad Test where
return a = Test $ \i -> (i, a)
(Test m) >>= f = Test $ \i -> let (i', a) = m i
in unTest (f a) $ i'
哪个符合monad法*。令人高兴的是,这已经存在于Control.Monad.State
。
** modulo seq
答案 2 :(得分:0)
摆脱第一种模式。您正在尝试为特定类型创建单独的定义,而这是您无法做到的。所有定义都需要适用于所有类型a
。如果你违反了这一点,第一个模式将与第二个模式相同,但a
绑定到[]
,所以只需写:
instance Monad Test where
Test (_, a) >>= k =
k a
return a =
Test (0, a)
然后 Test (0, []) >>= k
将匹配>>=
模式,并变为k []
,与您的第一个模式不必要地尝试完全相同。