为什么 Haskell 在将文字转换为我的类型时挂起?

Why does Haskell hang upon casting a literal to my type?

我已经为 Rational 编写了一个包装器类型,NaN 是除以零而不是使程序崩溃。代码编译时没有错误或警告。这是(希望是全部)相关代码:

data SafeRational =
    SRatio Rational |
    SRatioNaN

instance Show (SafeRational) where
    show (SRatio x) = show . fromRational $ x
    show SRatioNaN  = "NaN"

instance Num (SafeRational) where
    (+) (SRatio a) (SRatio b)   = SRatio (a+b)
    (+) _ _                     = SRatioNaN -- Good?
    (*) (SRatio a) (SRatio b)   = SRatio (a*b)
    (*) _ _                     = SRatioNaN
    signum (SRatio a)           = SRatio (signum a)
    signum SRatioNaN                = SRatio 0
    abs (SRatio a)              = SRatio (abs a)
    abs SRatioNaN               = SRatioNaN
    fromInteger a               = SRatio (fromInteger a)

instance Enum (SafeRational) where
    fromEnum (SRatio x)     = fromEnum x
    fromEnum SRatioNaN      = 0
    toEnum x                = SRatio $ toEnum x

instance Fractional (SafeRational) where
    (/) (SRatio a) (SRatio b)
        | b == 0    = SRatioNaN
        | otherwise = SRatio (a / b)
    fromRational a  = SRatio a

当我试图将一个负数 'cast' 转换为 SafeRational 时出现问题。 Haskell 挂了,好像进入了无限倒退。正数和零不会表现得很奇怪,只有小于零的数字才会。因为我使用 Haskell 作为数学工具,所以我不经常玩 class 声明,我担心我不知道如何调试问题。有人可以解释一下吗?

*GHCi> 0-2 :: SafeRational       -- makes Haskell sad
_

即使没有启用任何警告,我也会在将代码片段加载到 ghci (7.8.3) 时得到:

SRational.hs:9:10: Warning:
    No explicit implementation for
      either ‘negate’ or ‘-’
    In the instance declaration for ‘Num SafeRational’

即必须定义一个。它们的默认定义是相互递归的:

x - y               = x + negate y
negate x            = 0 - x

同时启用 -Wall 你发现了另一个错误:

SRational.hs:26:5: Warning:
    Pattern match(es) are non-exhaustive
    In an equation for ‘/’:
        Patterns not matched:
            SRatioNaN _
            (SRatio _) SRatioNaN

当你将任何东西除以 SRatioNaN 时会发生什么,例如0 / SRatioNaN ?


其他评论意见:

您可以通过以下方式定义节目:

instance Show (SafeRational) where
    show (SRatio x) = show x
    show SRatioNaN  = "NaN"

作为另一个 -Wall 警告提示,您(不必要地丢失信息)将 Rational 转换为 Double 之间。


(+) _ _                     = SRatioNaN -- Good?

是的,还

SRatio a + SRatio b         = SRatio (a + b)
_ + _                       = SRatioNan

读起来更好(恕我直言)。


为什么文字-2会变成fromInteger (-2 :: Integer)?原因在Haskell Report(3.4算子应用):

The special form -e denotes prefix negation, the only prefix operator in Haskell , and is syntax for negate (e). The binary - operator does not necessarily refer to the definition of - in the Prelude; it may be rebound by the module system. However, unary - will always refer to the negate function defined in the Prelude. There is no link between the local meaning of the - operator and unary negation.

所以-2转化为negate (fromInteger (2 :: Integer)).

这是 Haskell 的奇怪和讨论的功能:https://www.haskell.org/haskellwiki/Unary_operator https://ghc.haskell.org/trac/haskell-prime/wiki/NegativeSyntax