class 的 ghci 意外行为实例

ghci unexpected behavior instance of class

我一直在研究 Bryan O'Sullivan 和他的同事 "Real World Haskell,",并在 GHCi 7.8.3 版 [=25] 下遇到了我称之为意想不到的 'laxness' =]. 我“:加载”了以下 -

module JSONModule   where

data JValue = JNumber Double
           | JBool Bool
             deriving ( Show, Eq, Ord )


class JSON a where
  toJValue :: a -> JValue
  fromJValue :: JValue -> Either String  a


fromJBool (JBool b) = Right b
fromJBool _ = Left "not a JSON boolean"

instance JSON Double where
  toJValue = JNumber
  fromJValue = doubleToJValue id

instance JSON Bool where
  toJValue = JBool
  fromJValue = fromJBool

doubleToJValue :: (Double -> a) -> JValue -> Either String a
doubleToJValue f (JNumber v) = Right (f v)
doubleToJValue _ _ = Left "not a JSON number"

然后,在 ghci 中:

*JSONModule> :r
[1 of 1] Compiling JSONModule       ( JSONModule.hs, interpreted )
Ok, modules loaded: JSONModule.
*JSONModule> toJValue False
JBool False
*JSONModule> fromJValue it
Left "not a JSON number"

虽然这是事实,但这并不是人们所期望的。我认为 ghci 应该告诉我放风筝,因为有 2 个 fromJValue 实例。 事实上,如果我指定

fromJValue it :: Either String Bool

我猜对了,错了。问题似乎是 doubleToJValue。消除 JSON Double 实例,并向 JValue 添加 JChar Char 构造函数,以及相应的 JSON Char 实例,我从 ghci 得到了预期的 'ambiguous' 响应。 所以我认为有一个错误。 注释?谢谢...

看看到底发生了什么:

[1 of 1] Compiling JSONModule       ( test.hs, interpreted )
Ok, modules loaded: JSONModule.
>:set -Wall
>:t fromJValue (toJValue False)
fromJValue (toJValue False) :: JSON a => Either String a
> fromJValue (toJValue False)

<interactive>:6:2: Warning:
    Defaulting the following constraint(s) to type `Double'
      (JSON a0) arising from a use of `it' at <interactive>:6:2-28
      (Show a0) arising from a use of `print' at <interactive>:6:2-28
    In the first argument of `print', namely `it'
    In a stmt of an interactive GHCi command: print it
Left "not a JSON number"

如您所见,ghc 默认将模糊类型变量设置为 Double。当没有 Double 的实例时,它给出模糊类型错误的原因是因为默认行为仅默认约束为 Integer 或 Double,因为这些是已被发现最有用(或常见)的​​情况。 More info on defaulting.

这不是错误,而是 ExtendedDefaultRules extension 的结果,它在 GHCi 提示符下默认启用,但在文件中不启用。

大约,当一个类型在其他方面不明确并且具有 class 正确形式的约束时,具有此扩展的 GHC 将尝试将其默认为适合 (), Integer, Double.[=15 的第一个类型=]

没有ExtendedDefaultRules扩展名,比如默认在模块文件中,默认还是可以的,但是要求更严格(至少要有一个数字class,() 未尝试过)并且仅适用于一组固定的 classes,不适用于您自己定义的任何内容。