What is the recommended way to deal with non-polymorphic equality

Migrating my code to the newer Base/Core 0.13, and have to fix some usage of the polymorphic equality. While the simple cases of basic and library types are obvious, what is the recommended way to deal with custom sum types? Something like:

type mtyp = First | Second | Third | Infinity
let unexpected = mvar = Infinity

Another question, how to deal with other library-defined functions, e.g. I had the code like

if `Yes = Sys.file_exists path then ...

Base and Core themselves use ppx_compare for this. So your example would become:

type mtyp = First | Second | Third | Infinity [@@deriving equal, compare]
(* Via ppx, you now have:
    val equal_mtyp : mtyp -> mtyp -> bool
    val compare_mtyp : mtyp -> mtyp -> int
*)
let unexpected = equal_mtyp mvar Infinity

For a function like Sys.file_exists, I would probably just do

match Sys.file_exists path with
| `Yes -> ...
2 Likes

Depending on the exact case, you can also use pattern matching to avoid all equality concerns:

let unexpected = function
  | Infinity -> true
  | First -> false
  | Second -> false
  | Third -> false

This is especially useful if someday you add a new constructor such as Negative_infinity that should make unexpected return true as well. Compilation will break, forcing you to revisit these decisions for the new constructor (where using equality would silently default to returning false - sometimes it’s what you want, sometimes not).

2 Likes

If you can provide the compare function for that type, then this is recommended. The ppx_compare generator provides an easy to use generators. Otherwise, using the polymorphic compare function is still fine, especially when your data constructors do not have any payload. You can access the polymorphic comparison using Caml.compare, Stdlib.compare, or through the Polymorphic_compare module which you can open in places where you think that using the polymorphic comparison is admissible.

For that particular case, the inline comparison function generator is useful, e.g.,

if [%compare.equal : [`Yes | `No]] `Yes @@ Sys.file_exist path then ...

but if you find it too verbose, then again Polymorphic_compare.equal is still at your service. There is nothing wrong in the explicit use of the polymorphic comparison function, as long as you understand the consequences. All the problems arise from the accidental usages of it.