2

I have the following discriminated union:

type ActCard = Cellar of card list 
                | Chapel of (card option * card option* card option* card option) 
                | Smithy | Spy of (card -> bool * card -> bool)

It had structural equality until I added the card -> bool to Spy. This question is helpful for how to do custom equality for records. However, I'm not sure how best to implement it in this situation. I would prefer to not have to enumerate each case in ActCard:

override x.Equals(yobj) =
    match x, yobj with
    |  Spy _, Spy _ -> true
    |  Cellar cards, Cellar cards2 -> cards = cards2
    (* ... etc *)

What is a better approach here?

Community
  • 1
  • 1
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699

1 Answers1

9

There isn't a better approach. If you're not going to use the default structural equality you'll have to spell out equality semantics.

EDIT

You could do something like this.

[<CustomEquality; CustomComparison>]
type SpyFunc = 
  | SpyFunc of (card -> bool * card -> bool) 
  override x.Equals(y) = (match y with :? SpyFunc -> true | _ -> false)
  override x.GetHashCode() = 0
  interface System.IComparable with
    member x.CompareTo(y) = (match y with :? SpyFunc -> 0 | _ -> failwith "wrong type")

type ActCard = 
  | Cellar of card list 
  | Chapel of (card option * card option * card option * card option) 
  | Smithy 
  | Spy of SpyFunc
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • What if I want ActCard to support comparison? Is it necessary to provide a comparison for `SpyFunc`? – Nick Heiner Jun 13 '12 at 18:16
  • Yes. You'll need to use the `CustomComparison` attribute and implement `System.IComparable`. I updated my answer to demonstrate. – Daniel Jun 13 '12 at 18:28
  • Excellent, thanks. How can you apply those attributes to a type when it's part of an `and` binding? – Nick Heiner Jun 14 '12 at 03:25
  • Put the attributes after `and`. For example, `type A() = class end and [] B() = class end`. – Daniel Jun 14 '12 at 14:15