[英]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没有隐式转换。 因此,即使两个值都是Num
和Ord
实例,如果它们属于不同类型,您将无法将它们与(<)
进行比较。
您正在尝试将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.