Reason
  • Docs
  • Try
  • API
  • Community
  • Blog
  • Languages iconDeutsch
    • 日本語
    • English
    • Español
    • Français
    • 한국어
    • Português (Brasil)
    • Русский
    • Українська
    • 中文
    • 繁體中文
    • Beim Übersetzen helfen
  • GitHub

›Language Basics

Intro

  • What & Why

Setup

  • Installation
  • Editor Plugins

Language Basics

  • Overview
  • Let Bindings
  • Primitives
  • Basic Structures
  • Types
  • Records
  • Variants
  • Options and nullability
  • Functions
  • Recursion
  • Destructuring
  • Pattern Matching
  • Mutable Bindings
  • Loops
  • Modules

Advanced Features

  • JSX
  • External
  • Exception
  • Object

JavaScript

  • Interop
  • Syntax Cheatsheet
  • Pipe First
  • Promise
  • Libraries
  • Converting from JS

Extra

  • Frequently Asked Questions
  • Extra Goodies
Translate

Variants

Quick overview: Variants

Variant types model values that may assume one of many known variations. This feature is similar to "enums" in other languages, but each variant form may optionally specify data that is carried along with it.

This is a very powerful feature of the Reason language when used with pattern matching.

Creation

Variants require a type definition that specifies all of the "constructors", or "tags", for that variant. Constructors must start with an uppercase letter.

Here the constructors for the animal variant are Cat, Dog, Horse, and Snake:

type animal =
  | Cat
  | Dog
  | Horse
  | Snake;

To create a value of type animal use one of the constructors:

let fluffy = Cat;
let spot = Dog;

Constructor Arguments

Variant constructors can have data associated with them:

type linkedList =
  | Node(int, linkedList)
  | Empty;

The constructors can now accept their data as arguments. Creating a linked list [1, 2, 3] looks like:

let x = Node(1, Node(2, Node(3, Empty)));

Note: This is how the core list structure is actually implemented. The constructors are just hidden behind some syntax.

Usage

The primary way to interact with variants is through pattern matching. When pattern matching over a variant the compiler can ensure that all cases are covered exhaustively so that there is no undefined behavior. This is also helpful when adding new cases to a variant later, because the compiler will present a clear list of code that needs to be updated.

let interact = (animal) => {
  switch (animal) {
  | Cat => "pet"
  | Dog => "throw ball"
  | Horse => "ride"
  | Snake => "run away"
  };
};

It is possible to interact with a constructor's data while pattern matching:

let checkFirst = (linkedList) => {
  switch (linkedList) {
  | Node(value, _) => "First is:" ++ string_of_int(value)
  | Empty => "The list is empty"
  };
};

Option

Options are a commonly used variant built into the Reason language. They represent the presence or absence of a value. It is similar to the concept of "nullable" values in other languages.

Details: Options

Inline Records

Often times variants will hold a single predefined record type:

type cat = {
  name: string,
};

type dog = {
  breed: string,
};

type animal =
  | Cat(cat)
  | Dog(dog);

let x = Cat({name: "Fluffy"});

If that record type is only ever used within that single variant, it can be more concise to define the variant and record type at the same time using inline records:

type animal =
  | Cat({name: string})
  | Dog({breed: string});

let x = Cat({name: "Fluffy"});

Note: This is a somewhat recent language feature and not all online REPL's will support this syntax. Any recent version of Reason will support inline records though.

Tips

Unbound Constructor Error

The variant type needs to be in scope when referencing any constructor of that variant. An easy way to fix this error is with an explicit type annotation:

module Animal = {
  type t =
    | Cat
    | Dog
    | Horse
    | Snake;
};

/* Error: Unbound constructor */
let x = Cat;

/* No error */
let y: Animal.t = Cat;

Tuples as Arguments

There is a subtle difference between constructors that accept two arguments and constructors that accept one argument that is a 2-tuple. Pay close attention to which kind of variant you are dealing with:

type oneArgument =
  | OneArg((int, string));

type twoArguments =
  | TwoArgs(int, string);

let x = OneArg((1, "one"));
let y = TwoArgs(2, "two");

Using a one-argument variant that accepts a 2-tuple is common when dealing with structures that use type parameters. list((int, int)) is used because list(int, int) is not valid.

Variants Must Have Constructors

In some cases it can be tempting to write a variant that is "un-tagged":

type t =
  | int
  | string;

let x: t = 100;
let x: t = "one";

The Reason language does not support this feature. All variants must have constructors. The above code is actually a syntax error. Instead write:

type t =
  | Int(int)
  | String(string);

let x: t = Int(100);
let x: t = String("one");
← RecordsOptions and nullability →
  • Creation
  • Constructor Arguments
  • Usage
  • Option
  • Inline Records
  • Tips
    • Unbound Constructor Error
    • Tuples as Arguments
    • Variants Must Have Constructors