Jose M Gonzalez
Jose M Gonzalez

Reputation: 359

comparing with equal operator in fsharp

If I have the next type:

type Color(r: float, g: float, b:float) =
  member this.r = r
  member this.g = g
  member this.b = b
  static member ( * ) (c1:Color, c2:Color) =
      Color (c1.r*c2.r, c1.g*c2.g, c1.b*c2.b)

  static member Zero = Color(0.0,0.0,0.0)

and I do:

let ca = Color(1.,1.,1.)
let cb = Color(1.,1.,1.)
ca = cb

I should obtain true, but the F# interactive via a script is giving me false Instead, If I define as:

let ca = Color(1.,1.,1.)
let cb = ca
ca = cb

It returns true Am I doing something wrong trying to compare two values of a defined type in this way? How can I do it to obtain true as a result?

Thanks

Upvotes: 3

Views: 1665

Answers (3)

Mark Seemann
Mark Seemann

Reputation: 233135

The OP definition of Color is a class. Classes have referential equality by default, just like in C#. That means they're only equal if they literally are the same object (points to the same memory address).

Only the functional data types in F# have structural equality. These include records, discriminated unions, lists, and a few other types.

It'd be more idiomatic to define Color as a record:

type Color = { Red : float; Green : float; Blue : float }

This type has structural equality built in:

> let ca = { Red = 1.; Green = 1.; Blue = 1. };;

val ca : Color = {Red = 1.0;
                  Green = 1.0;
                  Blue = 1.0;}

> let cb = { Red = 1.; Green = 1.; Blue = 1. };;

val cb : Color = {Red = 1.0;
                  Green = 1.0;
                  Blue = 1.0;}

> ca = cb;;
val it : bool = true

If you want to define multiplication and zero for the type, you can do that as well:

let (*) x y = {
    Red = x.Red * y.Red
    Green = x.Green * y.Green
    Blue = x.Blue * y.Blue }

let zero = { Red = 0.0; Green = 0.0; Blue = 0.0 }

This enables you to write, e.g.:

> let product = ca * cb;;

val product : Color = {Red = 1.0;
                       Green = 1.0;
                       Blue = 1.0;}

Upvotes: 5

Tarmil
Tarmil

Reputation: 11362

F# implements automatic memberwise comparison for records and unions, but not for classes. If you want to have it and construct values with the Color(r, g, b) syntax, you can use a single-case union. You will get pattern matching as a bonus (see my implementation of (*)).

type Color =
  | Color of r: float * g: float * b: float

  member this.r = let (Color(r, _, _)) = this in r
  member this.g = let (Color(_, g, _)) = this in g
  member this.b = let (Color(_, _, b)) = this in b

  static member (*) (Color(r1, g1, b1), Color(r2, g2, b2)) =
    Color(r1 * r2, g1 * g2, b1 * b2)

  static member Zero = Color(0., 0., 0.)

Upvotes: 3

chollida
chollida

Reputation: 7894

To start you should read this page:

http://blogs.msdn.com/b/dsyme/archive/2009/11/08/equality-and-comparison-constraints-in-f-1-9-7.aspx

It does a great job of illustrating how equality works in F#.

As to your specific issue, you are looking at the difference between Reference Equality and Structural Equality. You can add the following annotation

[<CustomEquality; CustomComparison>]

And you can add overloads to the Equals method override x.Equals(other) to do your memberwise comparision

Upvotes: -2

Related Questions