English for ReScript Developers
Learn the English vocabulary for ReScript: variants, the type system, JavaScript interop, and pipe-first syntax.
ReScript conversations often need to draw a clear line between what the sound, ReScript-native type system guarantees and what happens the moment code crosses the boundary into untyped JavaScript — that boundary is where most real bugs and most real discussions live.
Key Vocabulary
Variant — a type representing one of a fixed set of named cases, each optionally carrying data, used instead of string literals or loosely-typed unions.
“Instead of a string status field that could be typo’d, we used a variant with Pending, Shipped, and Cancelled — the compiler now rejects invalid states entirely.”
Sound type system — ReScript’s guarantee that if the code compiles, the types are actually correct at runtime, unlike TypeScript’s type system which allows some unsound escape hatches. “We don’t need defensive null checks here — the sound type system guarantees this value can’t be undefined if the code compiled.”
Bindings (external) — hand-written type declarations that describe the shape of an existing JavaScript library or API to the ReScript compiler, since it can’t infer types from raw JS. “The crash happened because our binding for that JS library was slightly wrong — the type system trusted a declaration that didn’t match reality.”
Pipe-first (->) — an operator that passes the left-hand value as the first argument to the function on the right, used to chain transformations in a readable left-to-right order.
“Instead of nesting five function calls, we rewrote it as a -> pipe chain, and the transformation reads top to bottom the way it actually executes.”
Pattern matching (switch) — a switch expression over a variant that the compiler checks for exhaustiveness, ensuring every case, including new ones added later, gets handled.
“Adding the new Refunded variant broke the build in three places — that’s the compiler forcing us to update every switch that handles order status.”
Common Phrases
- “Is this a variant, or are we still passing around a raw string that could be misspelled?”
- “Can we trust the sound type system here, or does this value come from an external binding that might be wrong?”
- “Is our binding for this library accurate, or is that where the runtime mismatch is coming from?”
- “Should we rewrite this as a pipe chain for readability, or does the nested call actually read fine?”
- “Did this switch become non-exhaustive after adding the new variant — do we need to handle that case?”
Example Sentences
Explaining a type-safety win in review: “We modeled the payment state as a variant instead of a boolean pair, so ‘paid and refunded’ isn’t even a representable state anymore.”
Describing an interop issue: “The bug wasn’t in our logic — our binding claimed this callback always returns a value, but the actual JS library can return undefined.”
Justifying a refactor: “We switched this transformation to pipe-first syntax so new team members can read the data flow left to right instead of untangling nested calls.”
Professional Tips
- Prefer a variant over a string or boolean flag whenever the set of valid states is fixed and known — it makes invalid states unrepresentable.
- Treat a mismatched binding as the prime suspect whenever a runtime bug appears despite code that “should” be sound — the type system can only be as correct as the binding it trusts.
- Use pipe-first syntax deliberately for multi-step transformations, and say so in review — it’s a readability choice, not just a style preference.
- Welcome a broken exhaustive switch after adding a variant as useful feedback — it’s surfacing every place that needs to be updated.
Practice Exercise
- Explain why ReScript’s type system is described as “sound” while TypeScript’s is not fully sound.
- Describe what an external binding is and why an incorrect one can cause a runtime bug despite type-checking passing.
- Write a sentence justifying the use of a variant instead of a string field.