GHC:为什么在使用let时类型歧义会消失



我有一个类型类

class (Monad f) => Test f where
  test :: () -> f ()

和一个实例

instance Test (ErrorT String (Identity)) where
  test pat = return pat

如果我运行一个引用此实例的monad堆栈,GHC无法找到我所说的monad(在Either String monad的do块中):

rhs' <- runIdentity $ runErrorT $ test rhs

产生错误消息:

Ambiguous type variable `f0' in the constraint:
  (Test f0) arising from a use of `test'
  ...

但如果我将部分test rhs绑定到一个变量:

let action = test rhs
rhs' <- runIdentity $ runErrorT $ action

它是有效的,即使这个变量没有在其他地方使用,所以不能推断出任何新的信息

如果我没有添加任何信息供类型检查器使用,这怎么可能呢?为什么它不能计算出等效的第一个公式的类型?还是这两种表述不对等?我在这里不理解Haskell类型检查器(或desugaring规则?)的哪些部分?

我正在使用扩展MultiParamTypeClassesFlexibleInstancesScopedTypeVariables

edit:我简化了这个例子,所以这个奇怪的问题在不需要我的其余代码的情况下发生(并且使用了更短的monad堆栈),但现在它看起来毫无意义。声明的完整上下文是:

doStuff :: (Map Int ()) -> Either String (Map Int ())
doStuff g = run (snd . head . Map.toList $ g) g where
 run :: () -> Map Int () -> Either String (Map Int ())
 run rhs g = do
  let action = test rhs 
  rhs' <- runIdentity $ runErrorT $  test rhs -- or: action
  return g

代码

doStuff :: (Map Int ()) -> Either String (Map Int ())
doStuff g = run (snd . head . Map.toList $ g) g where
 run :: () -> Map Int () -> Either String (Map Int ())
 run rhs g = do
  rhs' <- runIdentity $ runErrorT $  test rhs
  return g

doStuff :: (Map Int ()) -> Either String (Map Int ())
doStuff g = run (snd . head . Map.toList $ g) g where
 run :: () -> Map Int () -> Either String (Map Int ())
 run rhs g = do
  let action = test rhs
  rhs' <- runIdentity $ runErrorT $ action
  return g

应键入check而不会出现问题。问题是

doStuff :: (Map Int ()) -> Either String (Map Int ())
doStuff g = run (snd . head . Map.toList $ g) g where
 run :: () -> Map Int () -> Either String (Map Int ())
 run rhs g = do
  let action = test rhs
  rhs' <- runIdentity $ runErrorT $ action
  return g

原因是您似乎启用了MonoLocalBinds或单态性限制,除非类型已知,否则它会阻止绑定到action的泛化。

单态性限制似乎让我绊倒了,再加上我在两个版本之间切换时在代码中留下了未使用的绑定(我以为我没有,也没关系,但确实如此)。因此CCD_ 8的类型不能归结为单态类型。

仍然有一个悬而未决的问题:在我的原始代码中,无论您现在看到(),我都使用多态类型(Pattern s)。编译器在错误消息中建议我为action添加一个类型签名来修复多态性,但令人困惑的是,我不能:即使使用了ScopedTypeVariables,我也得到了关于绑定在runaction的类型签名中的刚性类型变量的错误消息Couldn't match type 's1' with 's3' ...。但我想这是另一个问题,所以我认为这个问题已经得到了回答。非常感谢。

最新更新