What's the difference between generics/parameterized arguments and phantom types?


I am wondering what the difference is between generics/parameterized arguments and phantom types or whether they are the same?

Reference for generics: https://reasonml.github.io/docs/en/more-on-type
Reference for phantom types: https://medium.com/reasontraining/phantom-types-in-reasonml-1a4cfc18d999


My understanding is that phantom types are a pattern that is made possible by parameterized types.

You can use parameterized types without ever knowing about or using phantom types as the medium article suggests. Phantom types just use parameterized types to achieve their bit of trickery and enforce more strictness.

Hopefully, that makes sense!


Both features are unrelated, as far as I know. My mental model is to think of phantom types in terms of visibility, and about generics in terms of generalization.

For example, here is a very small case where a basic phantom type (i.e. non generic) is used to validate a string:

module Validator: {
  type t;
  let isValid: string => option(t);
} = {
  type t = string;
  let isValid = (s) => s === "valid" ? Some(s) : None;

This is a simplified example, but one could apply any validation to that input type. Note the module type definition doesn’t specify what t actually is (that is how a type becomes “phantom”), while the implementation specifies that t is a string.

So, inside the Validator module, t is equivalent to string. But outside of validator, it’s just Validator.t. If you try to use Validator.t as a string outside the module, the compiler will complain (example). And the other way around, you can build a bunch of functions that accept only “validated strings” (i.e. values of type Validator.t).

With phantom types, one can guarantee at the type system level that a specific piece of data has gone through some given functions in a specific order, which can be very useful in many scenarios.

After this, one might want to write a Validator module that validates different types (not only strings), so phantom types can be combined with generics, in the spirit of the ReasonTraining article that was linked above. But basic phantom types should go a long way.

I hope this helps! :ghost: