什么是 AllowAmbiguousTypes 以及为什么在此 "forall" 示例中需要它?

What is AllowAmbiguousTypes and why is it needed in this "forall" example?

代码

{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}

-- I know this particular example is silly.
-- But that's not the point here.
g :: forall a . RealFloat a => Bool
g = True

main :: IO ()
main = print (g @Double)

在 GHC 8.0 上编译失败并出现错误

• Could not deduce (RealFloat a0)
      from the context: RealFloat a
        bound by the type signature for:
                   g :: RealFloat a => Bool
        at app/Main.hs:3:6-35
      The type variable ‘a0’ is ambiguous
    • In the ambiguity check for ‘g’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      In the type signature:
        g :: forall a. RealFloat a => Bool

所以添加AllowAmbiguousTypes将使代码编译。

这是我的问题:

What exactly is AllowAmbiguousTypes?

来自latest GHC docs、"a type ty is ambiguous if and only if ((undefined :: ty) :: ty) would fail to typecheck"。扩展 AllowAmbiguousTypes 只是禁用此检查 - 它不会允许错误类型的程序通过。

Why is it needed to make this particular code work?

为了在使用 g 时检查 RealFloat a 是否满足,GHC 需要知道 a 是什么。你没有办法(在 vanilla Haskell1 中)告诉 GHC a 应该是什么,因为 a 在 [= 的类型中没有出现在其他地方16=]。再多的注释也不能让你使用 g 而不会出现类型变量不明确的错误。

但是,如果您不在任何地方使用 g,您仍然可以通过打开 AllowAmbiguousTypes.[=45 来编译您的代码=]

I fear that adding AllowAmbiguousTypes is giving me more than I really want in this particular code. It sounds scary. It sounds like it will make Haskell's type system less safe, perhaps in other areas that have nothing to do with this particular code. Are these fears unfounded?

是的,它们是:歧义检查可以让您捕获不能(在普通 Haskell 中没有 TypeApplications1)的定义在不导致歧义类型变量错误的情况下使用。禁用此检查仅意味着当您使用表达式(或函数)而不是在其定义站点时,您将看到不明确的类型变量消息。

Are there any alternatives? In this case, it seems like Haskell is inserting a a0 type variable that I never asked for. Is there no extension to tell Haskell not to create these extraneous type variables - and only use those that I explicitly told it to add with my own explicit forall a?

a0 来自我在本答案开头提到的歧义检查。 GHC 只是使用名称 a0 来表明它不同于 a。歧义检查基本上只是尝试 typecheck

((undefined :: forall a. RealFloat a => Bool) :: forall a0. RealFloat a0 => Bool)

AllowAmbiguousTypes 删除此检查。我不认为有一个扩展只对具有显式 foralls 的类型签名禁用歧义检查(尽管这可能很整洁有用!)。

Would you say that AllowAmbiguousTypes is a misnomer? Would it have been better named as AllowUnusedTypeVariables?

给事物命名很难。 :)

当前名称引用了您在未启用扩展程序的情况下遇到的错误类型,因此它不是一个坏名称。我想这是一个见仁见智的问题。 (很多人也希望 Monad 被称为 FlatMapAble。)


1TypeApplications(这是 GHC 8.0 的一个相对较新的扩展)之前,确实没有办法在不获取的情况下使用触发歧义检查的表达式模棱两可的类型变量错误,因此 AllowAmbiguousTypes 的用处不大。