Haskell:对Double类型的约束函数仅适用于整数

时间:2010-03-14 16:33:23

标签: haskell types integer double

假设我正在编写一个获取整数列表的函数,并仅返回列表中小于5.2的整数。我可能会这样做:

belowThreshold = filter (< 5.2)

够简单吧?但是现在我想将这个函数限制为只使用类型为[Int]的输入列表,这是出于我自己的设计原因。这似乎是一个合理的要求。唉,没有。一个声明,将类型限制为:

belowThreshold :: [Integer] -> [Integer]
belowThreshold = filter (< 5.2)

导致类型错误。那么这里的故事是什么?为什么做过滤器(&lt; 5.2)似乎将我的输入列表转换为双打?如何创建仅接受整数列表且仅返回整数列表的此函数的版本?为什么类型系统讨厌我?

4 个答案:

答案 0 :(得分:8)

在添加annoatation之前检查ghci中推断的belowThreshold类型:

> :t belowThreshold
belowThreshold :: [Double] -> [Double]

当你说“约束这个功能”时,听起来好像是你期望Num a => [a] -> [a]。实际上,在添加[Integer] -> [Integer]注释时,您正在更改函数的类型。

要使其工作,请使用显式转换:

belowThreshold = filter ((< 5.2) . fromIntegral)

现在belowThreshold :: [Integer] -> [Integer]就像你想要的那样。但是在与5.2比较之前,整数被转换为双精度。

那你为什么需要转换呢?类型错误可能误导了你:与5.2相比,整数列表没有被转换为双打,真正的问题是只有双打可以与双打比较,所以你必须< / em>将双打列表传递给belowThreshold。 Haskell没有隐式转换,甚至没有数字之间的转换。如果您想要转换,您必须自己编写。

  

我想将此函数限制为仅使用类型为[Int]的输入列表,这是出于我自己的设计原因。这似乎是一个合理的要求。

那么,从类型系统的角度来看,没有。这是合理的代码吗?

'c' < "foo"

这个怎么样?

12 < "bar"

所有这些值都是Ord的实例,但您不能将它们与(<)一起使用。 Haskell没有隐式转换。因此,即使两个值都是NumOrd的实例,如果它们属于不同类型,您将无法将它们与(<)进行比较。

答案 1 :(得分:3)

您正在尝试将Integer与double(5.2)进行比较。哈斯克尔不喜欢这样。尝试使用

filter (< 6)

答案 2 :(得分:1)

如果你必须使用double(假设它是一个参数),我会使用ceiling

filter (< (ceiling 5.2))

现在,如果您想要一个将边界值作为“任意”(相关)数值的函数,您可以创建自己的类型类来为您设置数字。

class Ceilingable a where
  ceil :: (Integral b) => a -> b

instance (RealFrac a) => Ceilingable a where
  ceil = ceiling

instance (Integral a) => Ceilingable a where
  ceil = fromIntegral

belowThreshold :: (Ceilingable a) => a -> [Integer] -> [Integer]
belowThreshold threshold = filter (< ceil threshold)

答案 3 :(得分:1)

语法5.2对任何Fractional都有效。 Int不是Fractional的实例,也不是或不应该是Rational的实例。在将任意Int转换为Double时要做的事情是不明确的。

从任意分数转换为fromIntegral然而非常有意义(在类型的范围内)。

你的期望是由于许多语言中隐含强制的存在所致。

然而,这些都带来了成本。您必须手动确保整个强制系统是融合的。 Haskell不会这样做,而是选择让数字文字语法利用类型系统。要在它们之间进行转换,您需要使用belowThreshold = filter (\x -> fromIntegral x < 5.2) 明确需要强制,这样可以避免依赖汇合并允许程序员定义新的数字类型。

((double)x < 5.2)

这类似于在C ++中使用显式转换,如5.2。虽然,此声明仅适用于默认,因为Fractional可以用作任何 Num的成员,而'fromIntegral x'的结果是{{1} }},Fractional的超类,因此fromIntegral x < 5.2未指定,它只知道它需要比较相同类型的两个Fractional值,并选择Double作为合理的默认值,基于'默认'语句。

另请注意,Int不是唯一的Integral类型,因此上述方法适用于任何Integral值列表:

belowThreshold :: Integral a => [a] -> [a]