【发布时间】: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 -> Bool,Proxy 在这里。 -
作为替代方案,您可以使用
RealFloat a => Tagged a Bool,其中Tagged来自tagged包,在某些情况下可以比代理方式更有效。 -
AllowAmbiguousTypes,在我看来,是无害的。在许多 lambda 演算中,类型总是显式传递(例如map @a @b f xs)。在普通的 Haskell 中,类型是推断出来的——这意味着我们不必传递它们(好),即使我们想要也不能传递它们(坏)。因此,必须禁止具有无法从参数类型或返回值类型推断的 tyvar 类型。因此,我们只能使用代理/标记来使它们可推断,从而增加混乱。但是,现在 GHC 允许显式类型应用程序,因此我们不再需要这样做。
标签: haskell types ghc ambiguous-types