Skip to content

Syntax & Examples

Importing

The algebraic data type can be defined using @data macro. It can be imported from the Moshi.Data module:

using Moshi.Data: @data

The Moshi.Data module also defines a set of reflection functions to work with the algebraic data types. You can check Reflection for more information. All the name can be imported together using:

using Moshi.Data.Prelude

Quick Example

Here is a quick example of defining a simple algebraic data type:

@data Message begin
Quit
struct Move
x::Int
y::Int
end
Write(String)
ChangeColor(Int, Int, Int)
end

This defines a new algebraic data type Message with 4 variants: Quit, Move, Write, and ChangeColor. The Move variant has two fields x and y of type Int. The Write variant has a single field of type String. The ChangeColor variant has three fields of type Int.

You can create an instance of the Message type as follows:

Message.Quit()
Message.Move(10, 20)
Message.Write("Hello, World!")
Message.ChangeColor(255, 0, 0)

Formal Syntax

The syntax is as follows:

<data> := @data <ident> [ <supertype> ] begin
<variant>+
end
<variant> := <singleton> | <anonymous> | <named>
<singleton> := <ident>
<anonymous> := <ident> ( <type>+ )
<named> := struct <ident>
<field>+
end
<field> := <ident>::<type> [= <expr>]

<data> is the top-level syntax for defining an algebraic data type. It starts with the @data macro followed by the name of the data type. Optionally, it can have a supertype. The supertype can be any type that the data type extends. The begin keyword is used to start the body of the data type. The body consists of one or more variants.

A <variant> can be one of three types: <singleton>, <anonymous>, or <named>. A <singleton> variant is a variant with no fields. An <anonymous> variant is a variant with anonymous fields. A <named> variant is a variant with named fields.

Singleton Variant

The singleton variant is like an Base.@enum variant. It can be defined directly as:

<ident>

Unlike Base.@enum and rust enum, the singleton variant instance must be constructed explicitly with an empty constructor:

<ident>()

Anonymous Variant

The anonymous variant is useful when you want to define a variant with anonymous fields. It can be defined as:

<ident> ( <type>+ )

for example, the Write and ChangeColor variants in the above example are anonymous variants.

Named Variant

The named variant is just like normal Julia struct definition, except that it is defined inside the data type. It can be defined as:

struct <ident>
<field>+
end

for example, the Move variant in the above example is a named variant.

Generics/Type Parameters

The @data macro also supports defining algebraic data types with type parameters. For example:

@data Option{T} begin
Some(T)
None
end

This defines a new algebraic data type Option with two variants: Some and None. The Some variant has a single field of type T. The None variant has no fields.

The type parameters should be declared in the type definition

@data <ident>{<type>+} [ <supertype> ] begin
<variant>+
end

the syntax is the same as a normal struct type parameter declaration. Inside the begin ... end body, the type parameters can be used as normal types.

Default Pattern

The ADT defined with Moshi supports a default pattern when doing pattern matching. This is always the inverse operation of the generated constructor. Taking the Message example above:

@match message begin
Quit() => "Quit"
Move(x, y) => "Move to $(x), $(y)"
Write(msg) => "Write: $msg"
ChangeColor(r, g, b) => "Change color to ($r, $g, $b)"
_ => "Unknown"
end

the call pattern here does not need to match the exactly same number of arguments as the constructor. Intead if the pattern is Move(x) it is equivalent to Move(x, _).

For named variants, because it generates a @kwdef like constructor, the following keyword argument pattern is also supported:

@match message begin
Move(;y=10, x) => x
end