Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# -> Implement IComparable for HashSet<'a>

Tags:

f#

Is it possible to somehow implement IComparable for a HashSet<'a>? The reason for this is that I have following record declared:

[<StructuralComparison>]
type Category = { 
    mutable Id: string; 
    Name: string; 
    SavePath: string;
    Tags: HashSet<Tag> }

and Tag = { Tag:string; }

As you can see, then Tags in the Category record is of type HashSet<Tag> - and in order to map a sequence of Categories to a Map, I'll need to implement the IComparable somehow... else it will just result in:

The struct, record or union type 'Category' has the 'StructuralComparison' attribute but the component type 'HashSet' does not satisfy the 'comparison'

Please note that I cant use anything else than a HashSet<'a> since the database I'm working with dosent understand any fsharp-ish lists at all.

like image 606
ebb Avatar asked Nov 29 '22 04:11

ebb


1 Answers

I'll assume you want to compare and equate Categorys by taking only Id, Name, and SavePath into account (in that order), making the record behave as though Tags wasn't present:

open System
open System.Collections.Generic

[<CustomComparison; CustomEquality>]
type Category =
    { mutable Id : string;
      Name       : string;
      SavePath   : string;
      Tags       : HashSet<Tag> }
    member private this.Ident = this.Id, this.Name, this.SavePath
    interface IComparable<Category> with
        member this.CompareTo other =
            compare this.Ident other.Ident
    interface IComparable with
        member this.CompareTo obj =
            match obj with
              | null                 -> 1
              | :? Category as other -> (this :> IComparable<_>).CompareTo other
              | _                    -> invalidArg "obj" "not a Category"
    interface IEquatable<Category> with
        member this.Equals other =
            this.Ident = other.Ident
    override this.Equals obj =
        match obj with
          | :? Category as other -> (this :> IEquatable<_>).Equals other
          | _                    -> false
    override this.GetHashCode () =
        hash this.Ident

and Tag = { Tag : string; }

However, if instead you want to compare by Name and equate by Id then consider the following:

open System
open System.Collections.Generic

[<CustomComparison; CustomEquality>]
type Category =
    { mutable Id : string;
      Name       : string;
      SavePath   : string;
      Tags       : HashSet<Tag> }
    interface IComparable<Category> with
        member this.CompareTo { Name = name } =
            this.Name.CompareTo name
    interface IComparable with
        member this.CompareTo obj =
            match obj with
              | null                 -> 1
              | :? Category as other -> (this :> IComparable<_>).CompareTo other
              | _                    -> invalidArg "obj" "not a Category"
    interface IEquatable<Category> with
        member this.Equals { Id = id } =
            this.Id = id
    override this.Equals obj =
        match obj with
          | :? Category as other -> (this :> IEquatable<_>).Equals other
          | _                    -> false
    override this.GetHashCode () =
        this.Id.GetHashCode ()

and Tag = { Tag : string; }
like image 110
ildjarn Avatar answered Dec 04 '22 13:12

ildjarn