重叠实例错误的原因是什么?

Pau*_*-AG 3 haskell

此示例中出现“ToJSON [XYZ] 的重叠实例”错误的原因是什么:

data XYZ = XYZ Int
instance ToJSON [XYZ] where
  toJSON xs = ...
Run Code Online (Sandbox Code Playgroud)

GHC 显示为重叠实例:

instance ToJSON a => ToJSON [a] ...
Run Code Online (Sandbox Code Playgroud)

这不是一个合适的情况:还XYZ没有ToJSON实例。我可以用 修复它,{-# OVERLAPS #-}但我不明白为什么 GHC 不明白[a]wherea不需要ToJSON的显式定义ToJSON。我想念什么?

lef*_*out 5

\n

XYZ还没有ToJSON实例

\n
\n\n

你自己说 \xe2\x80\x93没有实例。无法确保稍后不会有人添加该实例。在这种情况下会发生什么?特别是,如果一段代码还没有看到实例,因此选择instance ToJSON a => ToJSON [a],而另一段代码稍后该实例,则您将同时使用两个可能不兼容的实例。浩劫。

\n\n

我认为你可能想要表达永远不instance ToJSON XYZ应该被定义,但实际上 Haskell 没有类型不是类实例的概念。假设每个类型都可能是每个类 \xe2\x80\x93 的实例,只是可能还无法看到实例。

\n\n

好吧,实际上可以通过自己定义一个无法实现的超类来禁用实例被真正定义的 \xe2\x80\x93 :

\n\n
instance Bottom => ToJSON XYZ where\n  toJSON = no\n
Run Code Online (Sandbox Code Playgroud)\n\n

toJSON...如果您尝试在任何地方调用,这将导致类型错误XYZ。但就语言而言,这仍然一个实例,它恰好是一个 \xe2\x80\x9cun-typecheckeable 实例\xe2\x80\x9d。

\n\n

因此,实例重叠的,即使重叠实际上可能不是可以编译 \xe2\x80\x93 的东西,但实例解析不会尝试检查它,即尝试编译它,这很快就会变得非常低效通常会导致相当不可预测的程序行为。(如果您曾经使用过高级 C++ 模板,您就会明白我的意思。)

\n\n

相反,实例查找始终只匹配实例头,即[a]vs. [XYZ]。现在很明显,这确实是重叠的。

\n\n

为您的应用程序做的正确的事情可能是制作一个

\n\n
newtype XYZs = XYZs { getXYZs :: [XYZ] }\n\ninstance ToJSON XYZs where\n  ...\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您发现语法上很尴尬,请注意您可以使用OverloadedLists扩展名

\n\n
import GHC.Exts (IsList(..))\n\ninstance IsList XYZs where\n  type Item XYZs = XYZ\n  fromList = XYZs\n  toList = getXYZs\n
Run Code Online (Sandbox Code Playgroud)\n\n

进而

\n\n
{-# LANGUAGE OverloadedLists #-}\ntest :: XYZs\ntest = [XYZ 1, XYZ 2]\n
Run Code Online (Sandbox Code Playgroud)\n\n

是合法的。

\n