假设我正在编写一个获取整数列表的函数,并仅返回列表中小于5.2的整数。我可能会这样做:
belowThreshold = filter (< 5.2)
够简单吧?但是现在我想将这个函数限制为只使用类型为[Int]的输入列表,这是出于我自己的设计原因。这似乎是一个合理的要求。唉,没有。一个声明,将类型限制为:
belowThreshold :: [Integer] -> [Integer]
belowThreshold = filter (< 5.2)
导致类型错误。那么这里的故事是什么?为什么做过滤器(&lt; 5.2)似乎将我的输入列表转换为双打?如何创建仅接受整数列表且仅返回整数列表的此函数的版本?为什么类型系统讨厌我?
答案 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没有隐式转换。因此,即使两个值都是Num
和Ord
的实例,如果它们属于不同类型,您将无法将它们与(<)
进行比较。
答案 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]