Haskell 错误 - 未解决的重载 - 因使用“print”而产生的类型变量“a0”不明确

Haskell ERROR - Unresolved overloading - Ambiguous type variable `a0' arising from a use of `print'

此问题基于“Haskell 从第一原则开始编程”一书中的示例。在关于类型类的章节中,展示了一个不正确的例子。我正在努力让它发挥作用:

class Numberish a where
  fromNumber :: Integer -> a
  toNumber :: a -> Integer
newtype Age =
  Age Integer
  deriving (Eq, Show)
instance Numberish Age where
  fromNumber n = Age n
  toNumber (Age n) = n

toNumber 函数有效,但fromNumber 无效。我使用 HUGS 时的错误是“错误 - 未解决的重载”。当我使用 GHCi 时,错误以“使用 'print' 引起的不明确类型变量 'a0' 阻止约束 '(Show a0)' 被解决”开始。

我尝试更改新类型并直接重新声明实例(并仅使用 deriving Eq 而不是同时使用 Eq 和 Show),但同样的错误再次出现:

newtype Age a =
  Age Integer
  deriving Eq
instance Show (Age a) where
  show (Age a) = "Age " ++ show a 
instance Show a => Numberish (Age a) where
  fromNumber n = Age n
  toNumber (Age n) = n

有没有办法让 fromNumber 起作用?比如我们输入fromNumber 1,输出应该是Age 1.

根据最新的答案,我根据我更改的代码尝试了 GHCi 中的新输入,并截屏了结果。 fromNumber 42 :: Age String 有效。

我也在基于原始示例代码的 GHCi 中尝试了 fromNumber 42 :: Age,它也有效。

在第一个示例中,问题出在表达式中的类型

fromNumber 5

GHCi 只知道结果类型是 Numberish 的某个实例,但它不知道是哪一个。它只有一个可供选择这一事实并不重要。

你可以告诉 GHCi 使用哪种类型,它会很高兴:

fromNumber 5 :: Age

如果您在实际程序中使用它,很可能会有其他东西告诉编译器它是哪种类型;你会用这个值做其他特定于年龄的事情,比如将它存储在记录中或将它作为参数传递给函数。在这种情况下会出现问题,因为该值未用于任何用途,因此编译器无法缩小范围。

第二部分的代码不太清楚;您给了 Age 一个未使用的类型参数。这称为 phantom type,我不确定您是否打算这样做。当您出于某些其他原因而不是了解内容而想要使用类型标记值时,将使用这些。所以第二个版本 fromNumber 42 :: Age String 应该可以工作,因为 String 有一个 Show 实例,正如你的 Numberish 实例所需要的。