Haskell:模式匹配绑定运算符时的刚性类型变量错误

时间:2013-10-24 14:04:32

标签: haskell pattern-matching monads

我正在尝试运行此

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很新,但不明白为什么这不起作用。


编辑:对不起,这是一个糟糕的例子。假设>> =的第一个定义给出了不同的输出。

3 个答案:

答案 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 [],与您的第一个模式不必要地尝试完全相同。