为什么这个Haskell程序会发散?

24

看起来 [1,2,3] 和 [1..3] 之间有一个奇怪的区别。

在以下代码中使用 runghc 执行会打印出一个 "True",然后就一直挂着了。为什么?(我正在使用 ghc 7.8.3)

module Main where

import Data.Functor((<$>))
import Data.Time.Clock(DiffTime)
import Data.Binary(Binary(..), encode, decode, Get)
import Data.Int(Int64)

instance Binary DiffTime where
  put x = put (truncate (x * 10^12) :: Int64)
  get = ((/ 10^12) . fromIntegral) <$> (get :: Get Int64)

prop_getput_difftime :: DiffTime -> Bool
prop_getput_difftime x = x == ((decode . encode $ x) :: DiffTime)

explicit :: [DiffTime]
explicit = [1,2,3,4,5,6,7,8,9,10]

elipsis :: [DiffTime]
elipsis = [1..10]

main :: IO ()
main = do
  print $ all prop_getput_difftime explicit
  print $ all prop_getput_difftime elipsis -- diverges!

请注意,上述对于DiffTime实现的二进制表示是错误的;但这并不是重点。

如果交换这些语句会发生什么? - user1804599
1个回答

33

感谢 #haskell 中的 otulp,以下是原因:

Prelude> take 3 [1..2] :: [Data.Time.DiffTime]
[1s,1.000000000001s,1.000000000002s]

这是由于 DiffTimeEnum 实例引起的。

elipsis 更改为以下内容可以解决问题:

elipsis = map fromIntegral ([1..10] :: [Int])

1
@PeterWortmann,不错!但是为了使其对不知情的维护人员具有未来可扩展性,该解决方案有点更加晦涩。 - sinelaw
2
Enum 实例本身非常晦涩。 - leftaroundabout
这两个“修复”措施对我来说看起来都很脆弱。我有什么遗漏的吗? - dfeuer
无论如何,[x..y] 都是一个巨大的陷阱。[x..y] 应该具有类型 Integral a => [a],并且应该有另一个函数 enumAll :: Enum a => a -> a -> [a] - sinelaw
以后参考,每当省略号给你带来问题时,请记住它只是“枚举”的语法糖,因此在所有情况下,这就是你想要查看并确保一切正常的地方。顺便说一句,我对“枚举”非常满意。 - MasterMastic

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接