Fuzion Logo
flang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

Fuzion Choice Type

Motivation

In many situations, data can have one of several forms. Imagine we want to define the data type for the a binary tree that stores 32-bit integer values in its leafs. Such a tree can be either the empty tree,
Empty { }
or it can be a single leaf containing a 32-bit integer value,
i32
or it could be a reference to a node with a left and a right sub-tree.
ref Node(l, r Tree) { }
For the definition of a Tree, fuzion provides choice types:
choice Tree
{
  Empty,
  i32,
  Node
}

Options

Often, data of a specific type can either be present or absent. For example, a function that looks up data the corresponds to a person of a given name, could return this data if a person with that name was found, or return nothing otherwise. In many languages, returning nothing is indicated by a special value such as null. However, this results in countless programming errors since it is not explicit that a given value is optional. Languages like Rust use an Option enum that provides a choice between Some(v) and None. Fuzion provides a similar choice type, but avoids the need to additionally wrap a value as is done with Some(v):
choice Option
{
  T,
  None
}

Switch statement syntax

In Rust, unwrapping an enum into its actual values is done with the match expression as follows:
fn size(tree: Tree) -> i32 {
    match tree {
        Empty     => 0,
        Leaf(i)   => i,
        Node(l,r) => sum(l) + sum(r),
    }
}
In Fuzion, the different values a choice type could take are feature types already, there is no need to unwrap them. A decision to make is whether new identifiers should be introduced for the specific types, e.g., with new identifiers l and n
i32 sum(Tree t) {
    result = match t {
                 Empty  => 0,
                 i32  l => l,
                 Node n => sum(n.l) + sum(n.r),
             };
}
or without new identifiers, but with a re-interpretation of t's static type to match the actual case
i32 sum(Tree t) {
    result = match t {
                 Empty => 0,
                 i32   => t,
                 Node  => sum(t.l) + sum(t.r),
             };
}
Using an extension of the undeservedly disliked ternary ? : operator, we can achieve a very concise syntax
i32 sum(Tree t) {
    result = t ? Empty => 0
               : i32   => t
               : Node  => sum(t.l) + sum(t.r);
}
Using , instead of :, the syntax would be closer to that of a match expression
i32 sum(Tree t) {
    result = t ? Empty => 0,
                 i32   => t,
                 Node  => sum(t.l) + sum(t.r);
}

Feature declarations in a choice declaration

For operations that work on a choice value, it might make sense to declare corresponding features directly locally within the choice declaration, such as
choice Tree
{
  Empty,
  i32,
  Node;

  i32 size
  {
    outer ? Empty  => 0,
            i32    => 1,
            Node n => size(n.l) + size(n.r)
  }
}

Choice as a specific feature

One way to think about a choice is as of a generic class with an open list of generic parameters T1, t2, etc. and, conceptually, one field for each generic type, i.e.,
choice  {
  private T1 value1;
  private T2 value2;
  [..]
}
together with some logic that ensures that exactly one of the fields value1, value2, etc. is set and accessible. Another way to look at this is like a union type in C, but equipped with type safety that ensures that only the valid value can be accessed. Then, the definition of a concrete choice becomes a feature that inherits from choice:
Tree : choice 
{
  i32 size
  {
    outer ? Empty  => 0,
            i32    => 1,
            Node n => size(n.l) + size(n.r)
  }
}
Note that outer refers to the value of the feature surrounding size, the value of Tree. This would even allow choices to be extensible, e.g. to define an US traffic light
red {}
yellow {}
green {}

traffic_light : choice  {}
European traffic lights typically show red and yellow together before they turn green, so we need an additional state, which can be done as follows
red_and_yellow {}

european_traffic_light : choice  {}
Using the specific feature choice with generic parameters, the Option choice defined above would look as follows:
Option: choice
{
  boolean isPresent
  {
    outer ? T    => true,
            None => false
  }
}
or, using a new operator "choice ? type" that is true iff the given choice value at runtime is the given type (which is a bit like Java's instanceof operation).
Option: choice
{
  bool isPresent
  {
    outer ? T
  }
}

Algebraic data types

As we have seen in the section Fuzion Features, features provide a means to declare Product types. To implement Algebraic data types, the choice types provides the missing Composite data type.