对受歧视的联合实施 F# 比较

Implementing F# comparison on disciminated unions

我有一个日志级别类型:

type LoggingLevel =
| Trace
| Debug
| Info

我想说的是某些日志记录级别高于其他级别。例如,Trace 高于 Info

所以我这样实现了IComparable

[<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)

但是当我尝试使用它时,出现错误:

if a >= b 
then 
  // ...

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

我这里怎么出错了?


我设法让它工作了,但是现在类型定义太冗长了!一定有更好的方法...

[<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)

我认为你的比较实现是基于输入的。以下为我编译:

[<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"

请注意,在这种情况下,other 将属于 obj 类型,您需要进行相应的装箱。由于这里所有的案例都是空的(即缺少类型)

我很想看到您尝试使用此逻辑的更完整示例。我怀疑 match 表达式可能更好,并允许您删除此自定义比较。

也就是说,在不知道您的确切用例的情况下,这样的东西不会更实用(也许)更简单吗?

type LoggingLevel = Trace | Debug | Info

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

let result = Logger.doSomeLogging Trace

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

您是否需要使用自定义相等和自定义比较? F# 为可区分联合内置了这些。你只需要在类型定义中按递增的顺序编写它们:

type LoggingLevel =
  | Info
  | Debug
  | Trace // Note the order here! 

Trace > Info // true

let levels = [ Trace; Debug; Info; Trace; Debug; Info ]

levels |> List.sort
// [Info; Info; Debug; Debug; Trace; Trace]
// Comparison ✔

levels |> List.countBy id
// [(Trace, 2); (Debug, 2); (Info, 2)]
// Equality ✔

更多信息: