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
这里重要的是 (>>=)
的调用者(而不是实现者)可以选择 a
和 b
的值;例如,我可能会像使用它一样使用它 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
你的实现中的问题是你有一个 a
,Working
构造函数中的第二个字段,你需要一个 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
就足够了,因为它不关心参数化的类型。
为什么这不是正确的实现?
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
这里重要的是 (>>=)
的调用者(而不是实现者)可以选择 a
和 b
的值;例如,我可能会像使用它一样使用它 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
你的实现中的问题是你有一个 a
,Working
构造函数中的第二个字段,你需要一个 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
就足够了,因为它不关心参数化的类型。