Monad bind 坚持不同的类型?

Monad bind insists on different type?

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

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

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

data Lock a = Working Bool a

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

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

这里重要的是 (>>=) 的调用者(而不是实现者)可以选择 ab 的值;例如,我可能会像使用它一样使用它 type:

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

现在很清楚为什么你的实现不正确了:在

Working False int >>= _ = Working False int

您将返回 Lock Int 而不是 Lock Bool

调用方有一个 Lock a 和一个函数 a -> Lock b,您的 >>= 的实现必须将它们组合成一个 Lock b。您没有提供 Lock 的定义,但我认为它看起来像这样:

data Lock a = Working Bool a

你的实现中的问题是你有一个 aWorking 构造函数中的第二个字段,你需要一个 b 来产生一个 Lock b.你不能只是 return Working False a,因为那是 Lock a,这不是 >>= 对 return 的承诺。从 a 中获取 b 的唯一方法是通过用户函数 f,因此您别无选择,只能调用它。

添加 Broken 构造函数后这不是真的原因是,您现在可以轻松地为任何 b 构造一个 Lock b,因为 Broken不需要参数化类型的任何值。

你可以看到与

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

你写Nothing >>= f的时候,没有a可以调用函数,那么怎么构造一个Maybe b呢?同样,Nothing 就足够了,因为它不关心参数化的类型。