为什么会有这样的不同行为,带有“歧义类型..”错误在ghci中?

问题描述

此示例适用于ghci,加载此文件

import Safe

t1 = tailMay []

并放入ghci:

> print t1
nothing

但是,如果我们在以前的文件添加类似的定义,那么它将不起作用:

import Safe

t1 = tailMay []
t2 = print $ tailMay []

出现这样的错误

    * Ambiguous type variable `a0' arising from a use of `print'
      prevents the constraint `(Show a0)' from being solved.
      Probable fix: use a type annotation to specify what `a0' should be.
      These potential instances exist:
        instance Show Ordering -- Defined in `GHC.Show'
        instance Show Integer -- Defined in `GHC.Show'
        instance Show a => Show (Maybe a) -- Defined in `GHC.Show'
        ...plus 22 others

这是ghc的第三个样本,具有相同的错误

import Safe

t1 = tailMay

main = do
  print $ t1 []
  print $ t1 [1,2,3]

为什么?以及如何在没有显式类型注释的情况下修复第二个示例?

解决方法

这里的问题是tailMay []可以为任何Maybe [a]生成类型为a的输出,而print可以为以下类型生成Maybe [a]的输入任何a(在Show类中)。

当您组成“通用生产者”和“通用使用者”时,编译器不知道选择哪种类型a-可以是类Show中的任何类型。选择a可能很重要,因为原则上print (Nothing :: Maybe [Int])可以打印出与print (Nothing :: Maybe [Bool])不同的内容。在这种情况下,打印的输出将是相同的,但这仅仅是因为我们很幸运。

例如,print ([] :: [Int])print ([] :: [Char])将打印不同的消息,因此print []是不明确的。因此,GHC拒绝了它,并需要显式类型注释(或使用扩展名的类型应用程序@ type)。

那么,为什么GHCi会接受这种歧义?嗯,GHCi旨在用于快速实验,因此,作为一种便利功能,它将努力默认这些不明确的a。这是使用extended defaulting rules完成的,(我猜)原则上也可以通过打开该扩展名在GHC中将其打开。 但是,不建议这样做,因为有时默认规则可能会选择一些意外类型,从而使代码可以编译,但会产生有害的运行时行为。

此问题的常见解决方案是使用批注(或@ type),因为它为程序员提供了更多控制权,使代码更易于阅读,并且避免了意外。