Fractionalな数列の謎仕様

mapするとリストの長さが変わるという、大変愉快な事態に遭遇しました。

Prelude> length $ [0,10..35]
4
Prelude> length $ map (/ 5) [0,10..35]
5

なんじゃこりゃーと思っていろいろ試してみる。

Prelude> [0,10..35]
[0,10,20,30]

Prelude> [0,10..35] :: [Double]
[0.0,10.0,20.0,30.0,40.0]

Prelude> [0,10..34] :: [Double]
[0.0,10.0,20.0,30.0]                   -- roundされてるっぽい???

Prelude> [0,10..35] :: [Float]
[0.0,10.0,20.0,30.0,40.0]              -- Floatも同様

Prelude> :m Ratio
Prelude Ratio> [0,10..35] :: [Rational]
[0%1,10%1,20%1,30%1,40%1]              -- Rationalも同様

どうやら等差数列の記法からFractionalな型のリストを作ろうとするとおかしなことが起こるようです。
この部分のロジックはどうなってんだろう思ってPreludeのソースを追ってみると、numericEnumFromThenTo という関数に行きつきました。次のような仕様らしいです。

numericEnumFromThenTo   :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo e1 e2 e3 = 
    takeWhile pred (numericEnumFromThen e1 e2)
  where
    mid = (e2 - e1) / 2
    pred | e2 > e1   = (<= e3 + mid)
         | otherwise = (>= e3 + mid)

第三引数から公差の半分過ぎたとこまで含むらしい。気持ち悪っ。
このへんで議論されていますね。誤差対策ぽいけど、それにしたってこの仕様は何とかすべきだと思うなぁ。

減少列

あんまり関係ないけど、上のコードを見て減少列が書けることを初めて知った。

Prelude> [1 .. -3]
[]
Prelude> [1,0 .. -3]
[1,0,-1,-2,-3]