为什么你不能将Ints与Nums或Ords进行比较?

use*_*462 3 haskell compare typeclass

我有一个功能:

comp :: (Ord a) => a -> a -> Bool
comp a b = if a > b then True else False
Run Code Online (Sandbox Code Playgroud)

这有效.但如果我这样做:

comp :: (Num a) => Int -> a -> Bool
comp a b = if a > b then True else False
Run Code Online (Sandbox Code Playgroud)

或这个:

comp :: (Ord a) => Int -> a -> Bool
comp a b = if a > b then True else False
Run Code Online (Sandbox Code Playgroud)

我收到一条错误消息:

无法演绎(a~Int)

咦?Num和Ord不包括Int吗?Num和Ord不仅仅包括实数而不是复数吗?

为什么在解释器中我可以比较任何真实到任何int:

4 > 4.5 
False
Run Code Online (Sandbox Code Playgroud)

但我不允许定义一个将Ints与Nums或Ords进行比较的函数?

Ben*_*son 11

>期望两个Ord相同类型的实例.

ghci> :t (>)
(>) :: Ord a => a -> a -> Bool
Run Code Online (Sandbox Code Playgroud)

那种类型的签名不一样(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]
Run Code Online (Sandbox Code Playgroud)

每个实例都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)
Run Code Online (Sandbox Code Playgroud)

*为了迂腐,你可以设计一些表示二进制整数的方法作为布尔列表.这样的表示对于newtype具有自己的Num和的实例是一个很好的候选者Ord.


原因4 > 4.5是文字4是多态的.它可以代表任何数字类型的'4'值:

ghci> :t 4
4 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

同样,4.5可以采用任何小数类型(4.5 :: Fractional a => a).编译器足够聪明,可以意识到您正在将"任意数字"类型的值与"任何小数"类型的值进行比较,并使用最方便的具体类型,在本例中为Double.(Double被声明为default在标准的前奏.)因此,在表达4 > 4.5,4并且4.5都将是Double秒.

您可以Ord通过向其中一个操作数提供类型签名来强制比较使用特定具体类型的实例.

  • @ user2108462`Num`不是类型 - 它是类型的集合.你说很多次,"为什么我不能把Ints与Nums比较"或"为什么我不能将Ints转换成Nums"和类似的东西; 但这些事情没有意义!您可以通过各种方式将Int转换为另一种类型的值; 但是您不能将一种类型的值转换为以*类型*集合为特征的值.(另外:Haskell根本没有进行任何隐式转换.你可能希望它能做到,但可能需要付出代价;例如,这可能会使类型推断变得不可判定.) (8认同)
  • @ user2108462有更多的"Num"实例比你知道的多.有`Int`和`Double`,是的,但也有复数的实例; 懒惰的自然与可观察的无穷大; 可计算的实数; 分圆数; 多项式; 仅存在于计算(应用/一元)环境中的数字; Haskell AST的反思; 自动分化; 和更多.你怎么可能希望明智地比较任意一对这些东西呢?地狱,其中一些东西 - 比如复杂的数字 - 甚至没有一种明智的方式可以与*自己*进行比较! (8认同)
  • @ user2108462你可以这样做,但你必须使用`fromIntegral`转换它,它取任何整数类型的值并将其转换为任何数字类型的值.`fromIntegral ::(Integral a,Num b)=> a - > b` (3认同)
  • @ user2108462如果某个东西是"整数"(一个可能不适合"Double"的任意大整数)怎么办? (2认同)