Libmir Archive

mir.algebraic — Algebraic Types

mir.algebraic provides type-safe discriminated unions for D — Variant, TaggedVariant, and Nullable — with exhaustive pattern matching, BetterC support, and zero-overhead access.

mir.algebraic is the Mir take on algebraic data types. It provides a Variant (anonymous tagged union) and TaggedVariant (named-tag variant) with compile-time exhaustiveness checking for match.

Basic Variant

import mir.algebraic;

// Declare a variant type
alias Shape = Variant!(int, double, string);

Shape s = 3.14;

// Exhaustive pattern match
s.match!(
    (int i)    => writefln!"int(%d)"(i),
    (double d) => writefln!"double(%g)"(d),
    (string t) => writefln!"string(%s)"(t),
);

If you forget a case, the compiler tells you:

Error: none of the handlers in match handle type `string`

Nullable — optional value

import mir.algebraic : Nullable;

Nullable!int opt = 42;
Nullable!int empty;

opt.match!(
    (int v) => writeln("has value: ", v),
    () => writeln("empty"),
);

// Or use get with default:
int val = opt.get(0);  // 0 if empty

Nullable!T is Variant!(T, typeof(null)) — no separate Option type needed.

TaggedVariant — named tags

import mir.algebraic;

struct Circle  { double radius; }
struct Rect    { double w, h; }
struct Triangle{ double base, height; }

alias Shape = TaggedVariant!(
    ["circle",   Circle],
    ["rect",     Rect],
    ["triangle", Triangle],
);

Shape s = Circle(5.0);

double area = s.match!(
    (Circle c)   => 3.14159 * c.radius * c.radius,
    (Rect r)     => r.w * r.h,
    (Triangle t) => 0.5 * t.base * t.height,
);

Peeking without full match

Shape s = Rect(3.0, 4.0);

if (auto rp = s.peek!Rect)
    writeln("Rect area: ", rp.w * rp.h);

// Or trustedGet when you know the type:
Rect r = s.trustedGet!Rect;

Algebraic — recursive / self-referential types

Algebraic is the building block for recursive variants (e.g. AST nodes):

import mir.algebraic : Algebraic, This;

// JSON-like value type
alias JSON = Algebraic!(
    typeof(null),
    bool,
    long,
    double,
    string,
    This[],               // array of JSON (recursive)
    This[string],         // object (recursive)
);

This is a placeholder for the variant type itself, enabling recursive definitions without pointer indirection.

match vs visit

  • matchexhaustive: compile error if any type is unhandled
  • visitnon-exhaustive: unhandled types hit an assert(0) at runtime
// visit: only handle some types
s.visit!(
    (Circle c) => writeln("Circle"),
    // Rect and Triangle fall through to assert(0)
);

Equality and ordering

alias V = Variant!(int, double);

V a = 1, b = 1;
assert(a == b);   // true

V c = 1.0;
assert(a != c);   // different type tag

Comparison with other D sumtype libraries

Featuremir.algebraicsumtype (Paul Backus)std.variant
Exhaustive matchYesYesNo
BetterCYesYesNo
@nogcYesYesNo
Recursive typesYes (This)LimitedNo
Named tagsYes (TaggedVariant)NoNo
NullableYesVia Option!TNo

On this page