Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing F# comparison on disciminated unions

I have a type for logging levels:

type LoggingLevel =
| Trace
| Debug
| Info

I would like to say that some logging levels are higher than others. For example, Trace is higher than Info.

So I implemented IComparable like this:

[<StructuralEqualityAttribute>]
[<CustomComparisonAttribute>]
type LoggingLevel =
| Trace
| Debug
| Info

  interface IComparable<LoggingLevel> with
    override this.CompareTo other =
      let score x =
        match x with
        | Trace -> 0
        | Debug -> 1
        | Info -> 2
      (score this) - (score other)

But when I try to use it, I get an error:

if a >= b 
then 
  // ...

The type 'LoggingLevel' does not support the 'comparison' constraint. For example, it does not support the 'System.IComparable' interface

How have I gone wrong here?


I managed to get it working, but now the type definition is so verbose! There must be a better way...

[<CustomEquality>]
[<CustomComparisonAttribute>]
type LoggingLevel =
  | Trace
  | Debug
  | Info

  override this.Equals (obj) =
    match obj with
    | :? LoggingLevel as other ->
      match (this, other) with
      | (Trace, Trace) -> true
      | (Debug, Debug) -> true
      | (Info, Info) -> true
      | _ -> false
    | _ -> false

  override this.GetHashCode () =
    match this with
    | Trace -> 0
    | Debug -> 1
    | Info -> 2

  interface IComparable<LoggingLevel> with
    member this.CompareTo (other : LoggingLevel) =
      let score x =
        match x with
        | Trace -> 0
        | Debug -> 1
        | Info -> 2
      (score this) - (score other)

  interface IComparable with
    override this.CompareTo other =
      (this :> IComparable<LoggingLevel>).CompareTo (other :?> LoggingLevel)
like image 409
sdgfsdh Avatar asked Oct 24 '25 03:10

sdgfsdh


1 Answers

I think you're comparison implementation is off here based on the typing. The following compiles for me:

[<CustomComparison>]
[<StructuralEquality>]
type LoggingLevel =
| Trace
| Debug
| Info

    interface System.IComparable with 
        member this.CompareTo other = 
            0
            // replace 0 with your comparison logic here

let a = Trace
let b = Debug

if Trace > Debug then printfn "here"

Note that other in this case will be of type obj and you'll need to box accordingly. Made all the trickier by the fact that all cases here are empty (i.e. lacking type)

I would be curious to see a more complete example, of you attempting to use this logic. I suspect a match expression might be better and allow you to remove this custom comparison.

That said, without knowing your exact use case, wouldn't something like this be more functional (and perhaps) simpler?

type LoggingLevel = Trace | Debug | Info

module Logger =        
    let doSomeLogging logLevel =
        match logLevel with
        | Trace -> "trace"
        | Debug -> "debug"
        | Info -> "info"

let result = Logger.doSomeLogging Trace
like image 63
pim Avatar answered Oct 26 '25 06:10

pim