Skip to content

"Equality Comparison" section in the manual #503

Closed
@ryyppy

Description

@ryyppy

Alright, so the usage of == and === is not straight-forward. There's a lot of questions that need to be fully discussed, otherwise users are left with a lot of technical questions that aren't easy to answer.

This issue serves as a brain dump on the topic, so we can write up a proper manual page that discusses all the details of doing equality checks properly.

Differences JavaScript vs ReScript

ReScript tries to mimic JS syntax / conventions. Many eslint rulesets recommend using === for any kind of comparisons, because it prevents unintentional type coercion which causes a lot of weird bugs. In ReScript, unintentional type coercion is not really a problem, because the user is responsible for comparing values of the same type, but it's still good to mimic the syntax, since it's easier for JS users to constantly remember the rulesets of the operators.

For complex data structures (objects, etc), you'd usually need a proper equality function (Object.is, deepEquals, shallowEquals,...) function to compare those structures, otherwise there's a lot of factors that will impact the equality comparison.

ReScript == operator

The == operator in ReScript is different than in JS. Depending on specific heuristics, the compiler either compiles the equality check into a === comparison (which is the best case scenario) or, in case the compared types are complex (i.e. variants, poly variants, records,..), introduces a runtime call Caml_obj.caml_equal that will do a specific equality check.

I am not 100% what the Caml_obj.caml_equal check does in detail, but the heuristics for converting == to === is the following:

  • comparing int, float, string
  • comparing a value to a variant / poly variant constructor without a payload (user == None / value == #hello)
  • comparing two variant / poly-variant values that do not contain any constructors with payload
  • probably more cases I am currently not aware of. See my Playground Test for all the listed cases.

The biggest problem with caml_equal is that it's not easy to optimize, and the user might be better off writing their own equality check when handling specific data structures. The user is also required to understand how specific ReScript data structures are represented in JS (namely variants, poly variants).

ReScript === operator

The operator is mostly equivalent to the JS version — it will strictly compare basic data types, and check for referential equality when it hits complex types (objects etc). Problem here is that there are ReScript specific scenarios that may cause confusion, such as:

  • #hello(1) === #hello(1) will always eval to false, since each poly variant constructor creates a new object, therefore === will always compare two objects with different references, whereas...
  • #hello === #hello will actually do what it states, since poly vars without payload will compile to plain strings, so "hello" === "hello" will be true.

Overview what the manual should contain

  • == and === in ReScript
  • How == works, how it compares, and how caml_equal behaves (and when to use it?)
  • Comparison to JS' == / ===
  • Example on comparing records
  • Example on comparing variants / poly-variants
  • Maybe in the React -> useEffect docs: How to use complex data structures in the dependency array?

References:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions