问题描述:

So I am playing around with Haskell and noticed something which puzzles me. I defined a complex floating point data structure and wanted to used comparison operators on it. Initally I did this which worked fine:

data Cplx = Cplx Float Float deriving (Eq, Show)

instance Ord Cplx where

(<=) a b = (<=) (normCplx a) (normCplx b)

(>=) a b = (>=) (normCplx a) (normCplx b)

(<) a b = (<) (normCplx a) (normCplx b)

(>) a b = (>) (normCplx a) (normCplx b)

normCplx :: Cplx -> Float

normCplx (Cplx a1 a2) = sqrt( a1^2 + a2^2)

But I also noticed just declaring:

data Cplx = Cplx Float Float deriving (Eq, Show)

instance Ord Cplx where

(<=) a b = (<=) (normCplx a) (normCplx b)

normCplx :: Cplx -> Float

normCplx (Cplx a1 a2) = sqrt( a1^2 + a2^2)

Also got the job done just fine. How does Haskell infer the definitons for the other comparison operators given only the single definition?

Thanks!

网友答案:

the rest is implemented just with the type-class using (<=) (or compare) - this is what "minimal implementation" in the docs here: http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#t:Ord mean.

Minimal complete definition: either compare or <=. Using compare can be more efficient for complex types.

Sadly in this case you cannot show the source directly from Hackage but as Ord a implies Eq a it's not hard to imagine how this is done.

For example as you have (<=) implemented (<) can just be:

a < b = a /= b && a <= b

with that you get

a >= b = not (a < b)

and finaly

a > b = not (a <= b)

or something similar.

Btw as you might have seen in your first implementation you did not implement compare, max or min even if it's part of the type-class and this it got infered too.

Maybe you can try to guess it yourself.

remark

David found the sources - here is the implementation right from there:

class  (Eq a) => Ord a  where
    compare              :: a -> a -> Ordering
    (<), (<=), (>), (>=) :: a -> a -> Bool
    max, min             :: a -> a -> a

    compare x y = if x == y then EQ
                  -- NB: must be '<=' not '<' to validate the
                  -- above claim about the minimal things that
                  -- can be defined for an instance of Ord:
                  else if x <= y then LT
                  else GT

    x <  y = case compare x y of { LT -> True;  _ -> False }
    x <= y = case compare x y of { GT -> False; _ -> True }
    x >  y = case compare x y of { GT -> True;  _ -> False }
    x >= y = case compare x y of { LT -> False; _ -> True }

        -- These two default methods use '<=' rather than 'compare'
        -- because the latter is often more expensive
    max x y = if x <= y then y else x
    min x y = if x <= y then x else y
    {-# MINIMAL compare | (<=) #-}

As you can see: if you have compare or (<=) all else follows.

网友答案:

It must be said that the Ord typeclass is for types with total order, and complex numbers do not satisfy the properties of total order. In particular, your implementation violates the anti-symmetry property, that is,

forall a b. if a ≤ b and b ≤ a then a = b.

Failing to satisfy this property violates the contract of Ord and could result in strange bugs, depending on how the instance is used.

It might be more appropriate to apply normCplx where you might have relied on functionality derived from Ord, e.g., sortBy (compare `on` normCplx) where you might have just used sort.

相关阅读:
Top