GHC何时可以推断约束变量?

我得到类型推断错误,因为GHC不会推断出一个约束变量。 它看起来可以通过一阶统一来推断。 在进一步的调查中,我发现插入let-bindings会改变类型推断的行为。 我想知道GHC在做什么。

这里的代码演示了这个问题。 新类型ConstrainedF c表示一个多态函数,其类型参数受c约束。 据我所知,GHC不会根据给予ConstrainedF的值来推断c

{-# LANGUAGE RankNTypes, ScopedTypeVariables, ConstraintKinds, MonoLocalBinds #-}

import Data.Monoid
import GHC.Prim(Constraint)

newtype ConstrainedF c =
  ConstrainedF { runConstrainedF :: forall a. c a => [a] -> a}

applyConstrainedF :: forall c a. c a => ConstrainedF c -> [a] -> a
applyConstrainedF f xs = runConstrainedF f xs

-- GHC cannot infer the type parameter of ConstrainedF
foo :: [Int]
foo = applyConstrainedF (ConstrainedF mconcat) [[1], [2]]
--foo = applyConstrainedF (ConstrainedF mconcat :: ConstrainedF Monoid) [[1], [2]]

应该可以推断应用程序ConstrainedF mconcat中的类型:

  • ConstrainedF已经键入forall c. (forall a. ca => [a] -> a) -> ConstrainedF c forall c. (forall a. ca => [a] -> a) -> ConstrainedF c
  • mconcat有类型forall b. Monoid b => [b] -> b forall b. Monoid b => [b] -> b
  • forall b. Monoid b => [b] -> b forall b. Monoid b => [b] -> bforall a. ca => [a] -> a forall b. Monoid b => [b] -> b相一致forall a. ca => [a] -> a forall a. ca => [a] -> a通过赋值a := bc := Monoid
  • 但是,GHC抱怨说:

    Could not deduce (Monoid a) arising from a use of `mconcat'
    from the context (c0 a).
    

    关于约束变量,我必须遵循哪些规则,以便GHC能够推断类型?


    解决模糊类型错误的典型解决方案是添加代理值来约束模糊类型。 当我尝试时这很挑剔。 如果我只是添加一个额外的参数来约束c的类型,它会起作用:

    data Cst1 (c :: * -> Constraint) = Cst1
    
    monoid :: Cst1 Monoid
    monoid = Cst1
    
    applyConstrainedF :: forall c a. c a => ConstrainedF c -> Cst1 c -> [a] -> a
    applyConstrainedF f _ xs = runConstrainedF f xs
    
    foo :: [Int]
    foo = applyConstrainedF (ConstrainedF mconcat) monoid [[1], [2]]
    

    但是在foo引入let绑定混淆了类型推断,并且它不能再将Monoidc统一起来。

    foo_doesn't_work :: [Int]
    foo_doesn't_work = let cf = ConstrainedF mconcat
                       in applyConstrainedF cf monoid [[1], [2]]
    

    由于类型推断在这两个函数之一中得到正确的答案,这告诉我GHC将在某些情况下统一约束变量,而不是其他函数。 我不明白发生了什么事。


    这里的问题是子类型。 在你的例子中, c也可以是(Monoid b, Eq b)

    此外,你可以使用Data.Typeable来检查c被实例化了。

    或者,如果你要求用Monoid “统一” (c, d) (一对约束)?


    你问题的第二部分的答案是 - 你猜对了! - 让泛化。

    我知道你猜对了,因为你添加了一个MonoLocalBinds编译指示。 但是,这并不符合你期望的。 你看,它只能停止真正的本地绑定的泛化 - 那些依赖于函数参数或其他本地绑定的绑定。

    例如这可以工作:

    foo_does_work :: () -> [Int]
    foo_does_work x =
      let cf = const (ConstrainedF mconcat) x
      in applyConstrainedF cf monoid [[1], [2]]
    

    有关详细信息,请参阅让泛化:哪些绑定受到影响?

    链接地址: http://www.djcxy.com/p/43075.html

    上一篇: When can GHC infer constraint variables?

    下一篇: Odd ghc error message, "My brain just exploded"?