我有一个功能:
comp :: (Ord a) => a -> a -> Bool
comp a b = if a > b then True else False
这很有效。但如果我这样做:
comp :: (Num a) => Int -> a -> Bool
comp a b = if a > b then True else False
或者这个:
comp :: (Ord a) => Int -> a -> Bool
comp a b = if a > b then True else False
我收到错误消息:
无法推断(a~Int)
咦? Num和Ord不包括Int吗? Num和Ord不仅仅包括实数而不是复数吗?
为什么在解释器中我可以将任何实数与任何int进行比较:
4 > 4.5
False
但是我不允许定义一个将Ints与Nums或Ords进行比较的函数?
答案 0 :(得分:11)
>
需要两个Ord
个相同类型的实例。
ghci> :t (>)
(>) :: Ord a => a -> a -> Bool
该类型签名与(Ord a, Ord b) => a -> b -> Bool
不同,Ord
是一个函数的签名,可以比较任何两个>
实例。 (不存在这种功能的非平凡版本。)
Int
对可怜的程序员提出如此严厉的要求的原因是不同的类型可能具有不同的排序语义。您如何比较[Bool]
与Ord
?*即使它们都是ghci> 3 > [True, True, False]
<interactive>:6:1:
No instance for (Num [Bool]) arising from the literal `3'
Possible fix: add an instance declaration for (Num [Bool])
In the first argument of `(>)', namely `3'
In the expression: 3 > [True, True, False]
In an equation for `it': it = 3 > [True, True, False]
的实例,但比较它们并且编译器不允许它是没有意义的:
Ord
>
的每个实例都定义了自己的Int
版本。这就是您无法将Float
与ghci> (3 :: Int) > (4 :: Float)
<interactive>:4:15:
Couldn't match expected type `Int' with actual type `Float'
In the second argument of `(>)', namely `(4 :: Float)'
In the expression: (3 :: Int) > (4 :: Float)
In an equation for `it': it = (3 :: Int) > (4 :: Float)
进行比较的原因:
newtype
*为了迂腐,你可以设计一些表示二进制整数的方法作为布尔列表。这样的表示形式对Num
具有Ord
和4 > 4.5
的实例具有良好的候选者。
4
的作用原因是文字ghci> :t 4
4 :: Num a => a
是多态的。它可以代表任何数字类型的'4'值:
4.5
同样,4.5 :: Fractional a => a
可以采用任何小数类型(Double
)。编译器足够聪明,可以意识到您正在将“任意数字”类型的值与“任意小数”类型的值进行比较,并使用最方便的具体类型,在本例中为Double
。 (default
在标准前奏中被声明为4 > 4.5
。)
因此,在表达式4
中, 4.5
和Double
都是Ord
s。
您可以通过向其中一个操作数提供类型签名来强制比较使用特定具体类型的{{1}}实例。
答案 1 :(得分:4)
Ord
中的所有比较运算符都具有Ord a => a -> a -> Bool
类型,因此您只能比较两个相同类型的值。
为什么在解释器中我可以将任何实数与任何int进行比较:
4 > 4.5 False
你不是。您正在比较两个Double
。在Haskell中重写了整数文字,因此4
可以有任何Num
类型,具体取决于上下文。
> :t 4
4 :: Num a => a
十进制文字也是如此,它可以是任何Fractional
类型。
> :t 4.5
4.5 :: Fractional a => a
基于这些限制,以及双方应具有相同类型的事实,defaults both sides to Double
。
> :set -fwarn-type-defaults
> 4 > 4.5
<interactive>:6:5: Warning:
Defaulting the following constraint(s) to type `Double'
(Fractional a0)
arising from the literal `4.5' at <interactive>:6:5-7
(Num a0) arising from the literal `4' at <interactive>:6:1
(Ord a0) arising from a use of `>' at <interactive>:6:3
In the second argument of `(>)', namely `4.5'
In the expression: 4 > 4.5
In an equation for `it': it = 4 > 4.5
<interactive>:6:5: Warning:
Defaulting the following constraint(s) to type `Double'
(Fractional a0)
arising from the literal `4.5' at <interactive>:6:5-7
(Num a0) arising from the literal `4' at <interactive>:6:1
(Ord a0) arising from a use of `>' at <interactive>:6:3
In the second argument of `(>)', namely `4.5'
In the expression: 4 > 4.5
In an equation for `it': it = 4 > 4.5
答案 2 :(得分:3)
类型
comp :: (Num a) => Int -> a -> Bool
指出comp
可以使用Int
和任何数字类型,即调用此函数的人选择的数字类型。由于调用者可以选择复杂类型,因此无法以有意义的方式实现comp
。
类型
comp :: (Ord a) => Int -> a -> Bool
指出comp
可以处理Int
和任何有序类型,即由调用此函数的人选择的有序类型。由于来电者可以选择String
或(Int,Int)
,因此无法以有意义的方式实施comp
。
答案 3 :(得分:3)
好的,但是我们说你确实想写这个函数
comp :: (Num a, Ord a) => Int -> a -> Bool
您需要的功能是fromIntegral :: (Integral a, Num b) => a -> b
。 Haskell中Num
类型的一个属性是它必须实现这个函数的理智版本。最终的实现将是
comp n x = fromIntegral n < x