colinfang
colinfang

Reputation: 21727

Built-in "< > compare" doesn't work with "IComparable<T>"?

I have a Discriminated Union, and I hope to use built in operators like > < compare max for it.

[<CustomComparison>]
type SymbolType = 
    | A
    | B
    | C
    | D

    interface IComparable<SymbolType> with
        member x.CompareTo y =
            match x, y with
            | A, A-> 0
            | A, _ -> 1
            | _, A-> -1
            | _, _ -> 0

I understand I can use IComparable, but then i have to do a null check, what's worse is that I have to cast it like (SymbolType) y which I assume would be time consuming.

Upvotes: 0

Views: 322

Answers (3)

Tomas Petricek
Tomas Petricek

Reputation: 243051

You can already use standard comparison operators on the type. The built-in implementation uses the order of declarations of the individual cases, so:

type SymbolType =  A | B | C | D 

// Behavior of built-in comparison
A < B   = true
D <= C  = false
max B D = D

This looks very fragile, so maybe it is not the best thing to rely on. If you have cases that do not contain other values, you can use enum instead of discriminated union and define the ordering you wish:

type SymbolType =  
  | A = 1
  | B = 2
  | C = 4
  | D = 3

// The order is now defined by your code
SymbolType.C < SymbolType.D = false

Upvotes: 3

Need4Steed
Need4Steed

Reputation: 2180

On CLR, operators are static functions, so you can't define them in an interface. But boxing can also be avoided if your use the interface as a constraint of type parameter of a generic function.

int Compare<T>(T lhs, T rhs) where T : IComparable<T>
{
  return lhs.CompareTo(rhs) // no boxing
}

Sorry, I'm not familiar with F#, so I wrote the example in C#.

Upvotes: 0

John Palmer
John Palmer

Reputation: 25516

You can just implement the required methods with thin wrappers:

[<CustomComparison>] 
[<CustomEquality>] 
type SymbolType = 
    | A
    | B
    | C
    | D
    override x.Equals y =
       match y with
       | :? SymbolType as t -> (((x :> IComparable<_>).CompareTo) t)=0
       | _ -> false
    interface IComparable with
        member x.CompareTo y =
            match y with
            | :? SymbolType as t -> ((x :> IComparable<_>).CompareTo) t
            | _ -> failwith "bad comparison"
    interface IComparable<SymbolType> with
        member x.CompareTo y =
            match x, y with
            | A, A-> 0
            | A, _ -> 1
            | _, A-> -1
            | _, _ -> 0

This way does avoid any duplicate typing.

Upvotes: 1

Related Questions