【问题标题】:What is AllowAmbiguousTypes and why is it needed in this "forall" example?什么是 AllowAmbiguousTypes,为什么在这个“forall”示例中需要它?
【发布时间】:2017-12-21 01:28:37
【问题描述】:

代码

{-# 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 将使代码编译。

这是我的问题:

  • AllowAmbiguousTypes 到底是什么?
  • 为什么需要让这个特定的代码工作?
  • 我担心添加AllowAmbiguousTypes 给了我比我在这个特定代码中真正想要的更多。听起来很吓人。听起来这会使 Haskell 的类型系统变得不那么安全,也许在与此特定代码无关的其他领域。这些担心是没有根据的吗?
  • 还有其他选择吗?在这种情况下,Haskell 似乎插入了一个我从未要求过的a0 类型变量。有没有扩展告诉 Haskell 不要创建这些无关的类型变量 - 并且只使用我明确告诉它添加的那些我自己明确的 forall a
  • 由于user2407038 的评论而增加了一个问题:你会说AllowAmbiguousTypes 是用词不当吗?将其命名为AllowUnusedTypeVariables 会更好吗?

【问题讨论】:

  • 歧义类型是在其上下文中具有类型变量的类型,该类型变量在类型的主体(=> 右侧)中未提及。所以当.. 没有提到a 时,RealFloat a => .. 是模棱两可的。模棱两可的类型通常是程序员的错误,在 TypeApplications 之前它们完全没用,所以你需要一个扩展来允许它们。对于最后两个问题:它不会以任何方式使类型检查器“不安全”;你的替代方法是写RealFloat a => Proxy a -> BoolProxy 在这里。
  • 作为替代方案,您可以使用RealFloat a => Tagged a Bool,其中Tagged来自tagged包,在某些情况下可以比代理方式更有效。
  • AllowAmbiguousTypes,在我看来,是无害的。在许多 lambda 演算中,类型总是显式传递(例如 map @a @b f xs)。在普通的 Haskell 中,类型是推断出来的——这意味着我们不必传递它们(好),即使我们想要也不能传递它们(坏)。因此,必须禁止具有无法从参数类型或返回值类型推断的 tyvar 类型。因此,我们只能使用代理/标记来使它们可推断,从而增加混乱。但是,现在 GHC 允许显式类型应用程序,因此我们不再需要这样做。
  • 另见this question

标签: haskell types ghc ambiguous-types


【解决方案1】:

AllowAmbiguousTypes 到底是什么?

latest GHC docs,“当且仅当((undefined :: ty) :: ty) 无法进行类型检查时,类型ty 是模棱两可的”。扩展 AllowAmbiguousTypes 只是禁用此检查 - 它不会允许错误类型的程序通过。

为什么需要让这个特定的代码工作?

为了检查RealFloat a 在使用g 时是否满足,GHC 需要知道a 是什么。你没有办法(在 vanilla Haskell1 中)告诉 GHC a 应该是什么,因为 ag 类型的其他任何地方都不会出现。再多的注解都不会让您使用g 而不会出现模棱两可的类型变量错误。

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

我担心添加AllowAmbiguousTypes 给了我比我在这个特定代码中真正想要的更多。听起来很吓人。听起来这会使 Haskell 的类型系统变得不那么安全,也许在与此特定代码无关的其他领域。这些担心是没有根据的吗?

是的,它们是:歧义检查可让您捕获无法使用的定义(在 vanilla Haskell 中,它没有 TypeApplications1),而不会导致模棱两可的类型变量错误。禁用此检查仅意味着当您使用表达式(或函数)而不是在其定义站点时,您将看到模棱两可的类型变量消息。

还有其他选择吗?在这种情况下,Haskell 似乎插入了一个我从未要求过的a0 类型变量。是否没有扩展告诉 Haskell 不要创建这些无关的类型变量 - 并且只使用我明确告诉它添加的那些我自己明确的 forall a

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

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

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

你会说AllowAmbiguousTypes 是用词不当吗?将其命名为AllowUnusedTypeVariables 会更好吗?

给事物命名很难。 :)

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


1TypeApplications(这是 GHC 8.0 的一个相对较新的扩展)之前,确实没有办法使用触发歧义检查而不会出现歧义类型变量错误的表达式,所以AllowAmbiguousTypes 用处不大。

【讨论】:

  • since a occurs nowhere else in the type of g ← 不仅出现在类型中,而且不会出现在正文中。如果在函数体中使用a 变量,则不需要-XAllowAmbiguousTypes
  • @Shersh 你有什么想法?如果你在函数体中使用类型变量(大概是启用了ScopedTypeVariables),你就不会再通过类型检查器来解决调用站点上a 应该是什么...
  • 对不起。不知怎的,我错了。当我去年使用 ghc 8.0.1 测试这张幻灯片中的代码时,它起作用了。但是现在没有-XAllowAmbiguousTypes就不行了:slides.com/fp-ctd/lecture-15#/17 :(
  • 谢谢。几乎完美的答案。但是,您能详细说明一下为什么没有AllowAmbiguousTypes((undefined :: forall a. RealFloat a => Bool) :: forall a0. RealFloat a0 => Bool) 无法进行类型检查,但例如((undefined :: forall a. RealFloat a => a -> Bool) :: forall a0. RealFloat a0 => a0 -> Bool) 会成功进行类型检查?
  • @haskellHQ ((undefined :: forall a. RealFloat a => Bool) :: forall a0. RealFloat a0 => Bool) 无法进行类型检查,因为 GHC 无法统一两个类型变量(aa0)。在forall a0. RealFloat a => a -> Boolforall a0. RealFloat a0 => a0 -> Bool的情况下,GHC通过比较前两个参数实现a ~ a0
猜你喜欢
  • 1970-01-01
  • 2016-11-02
  • 1970-01-01
  • 2010-12-08
  • 1970-01-01
  • 1970-01-01
  • 2018-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多