【问题标题】:Why does Haskell hang upon casting a literal to my type?为什么 Haskell 坚持将文字转换为我的类型?
【发布时间】:2015-01-13 06:26:19
【问题描述】:

我已经为 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

当我尝试将负字面数字“转换”为SafeRational 时出现问题。 Haskell 挂起,就好像它进入了一个无限的回归。正数和零的行为并不奇怪,只有低于零的数字。因为我使用 Haskell 作为数学工具,所以我不经常玩类声明,恐怕我不知道如何调试问题。有人能解释一下吗?

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

【问题讨论】:

  • 提示 1:查看您对Num 的定义:您如何期望 Haskell 仅使用您提供的定义来计算减法?提示 2:如果您使用 GHC 7.8+,请打开所有警告 (-Wall)。
  • 请注意,在documentation for Num 中,它说:“最小完整定义:(+), (*), abs, signum, fromInteger, (negate | (-))
  • 那么 Haskell 会通过添加负数来产生负数吗?那是回归吗?为什么没有警告!另外,我使用 7.6.3。
  • 不,问题是因为您需要提供negate(-) 的定义,以便可以通过默认定义自动计算另一个。但是由于你没有定义任何一个,Haskell 将调用(-) 的默认定义,它调用negate 的默认定义,它调用(-) 的相同默认定义,无限......跨度>
  • 他们不是在最新的 GHC 中添加了一个编译指示来捕获这样的案例吗?

标签: haskell casting


【解决方案1】:

即使没有启用任何警告,我在将 sn-p 加载到 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?


其他评论cmets:

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

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 Operator Applications)中:

特殊形式 -e 表示前缀否定,是 Haskell 中唯一的前缀运算符,是 negate (e) 的语法。二进制-运算符不一定引用Prelude中-的定义;它可能会被模块系统反弹。但是,一元 - 将始终引用 Prelude 中定义的 negate 函数。 - 运算符的本地含义与一元否定之间没有联系。

所以-2 被转换为negate (fromInteger (2 :: Integer))

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

【讨论】:

  • 感谢您提供如此详细的答复。我使用'show'中的演员来获得十进制表示法。我已经修复了来自 -Wall 的警告。
  • 我还有一个问题:为什么 Haskell 做negate (SRatio 2) 而不是SRatio (-2)?如果不是这样,Haskell 是如何进行强制转换的?
  • 我添加了一个解释negate的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
  • 2021-05-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-17
  • 2018-11-04
相关资源
最近更新 更多