Monad绑定坚持不同的类型?

时间:2016-07-14 18:59:45

标签: haskell monads

为什么这不是正确的实施?

instance Monad Lock where
   (Working False x) >>= _ = Working False x
   (Working True  x) >>= f = f x

GHC吐出的错误是关于刚性类型变量的错误:

• Couldn't match type ‘a’ with ‘b’
  ‘a’ is a rigid type variable bound by
    the type signature for:
      (>>=) :: forall a b. Lock a -> (a -> Lock b) -> Lock b
    at src/Computers.hs:32:22
  ‘b’ is a rigid type variable bound by
    the type signature for:
      (>>=) :: forall a b. Lock a -> (a -> Lock b) -> Lock b
    at src/Computers.hs:32:22
  Expected type: Lock b
    Actual type: Lock a

我可能误解了错误,但是由于我的理解有限,编译器实际上需要我吐出不同的参数化类型,而不是相同的类型。

我尝试添加一个不带类型参数的不同构造函数(并且仅为测试更改语义) - 然后它工作正常:

instance Monad Lock where
   Broken            >>= _ = Broken
   (Working False x) >>= _ = Broken
   (Working True  x) >>= f = f x 

编辑: 实际上,Lock的定义是:

data Lock a = Working Bool a

2 个答案:

答案 0 :(得分:5)

我假设您对Lock的定义如下:

data Lock a = Working Bool a

现在,让我们看一下(>>=)的类型:

(>>=) :: Lock a -> (a -> Lock b) -> Lock b

重要的是,(>>=)(而不是实现者)的调用者可以选择ab的值;例如,我可能会使用它,就好像它有类型:

(>>=) :: Lock Int -> (Int -> Lock Bool) -> Lock Bool

现在很明显为什么你的实现是不正确的:在

Working False int >>= _ = Working False int

您将返回Lock Int而不是Lock Bool

答案 1 :(得分:2)

来电者有一个Lock a和一个a -> Lock b函数,您>>=的实施必须将这些内容合并为一个Lock b。您没有提供Lock的定义,但我认为它看起来像这样:

data Lock a = Working Bool a

您的实施中的问题是您有aWorking构造函数中的第二个字段,并且您需要b才能生成Lock b 。您不能只返回Working False a,因为这是Lock a,而不是>>=承诺返回的内容。从b获取a的唯一方法是通过用户的f功能,所以你别无选择,只能调用它。

添加Broken构造函数后,这种情况并非如此,您现在可以轻松地为任何Lock b构建b,因为Broken没有&# 39; t需要参数化的任何类型的值。

您可以通过

的实现看到相同的基本现象
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b

当您编写Nothing >>= f时,没有a来调用该函数,那么如何构建Maybe b?同样,Nothing就足够了,因为它并不关心参数化的类型。