繁体   English   中英

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

[英]Haskell: Constrain function on type Double to only work with Integers

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

belowThreshold = filter (< 5.2)

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

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

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

在添加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相比,整数列表没有被转换为双打,真正的问题是只能将双打与双打比较,所以你必须将双打列表传递给belowThreshold Haskell没有隐式转换,甚至没有数字之间的转换。 如果您想要转换,您必须自己编写。

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

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

'c' < "foo"

那这个呢?

12 < "bar"

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

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

filter (< 6)

如果你必须使用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)

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

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

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

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

belowThreshold = filter (\x -> fromIntegral x < 5.2) 

这类似于在C ++中使用显式转换,如((double)x < 5.2) 虽然,这个陈述只能起作用,因为违约,因为5.2可以用作任何 Fractional的成员,而'fromIntegral x'的结果是任何Num ,一个超级的Fractional ,所以fromIntegral x < 5.2是不明确的,它只是知道它需要比较相同类型的两个Fractional值,并根据'default'语句选择Double作为合理的默认值。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM