Menu

Zero Enums: Simple Enumerations Without Payloads

How enum works in Zero: declaring a fixed set of named variants, comparing values, and the line between enum (plain tags) and choice (tagged unions).

This page includes runnable editors - edit, run, and see output instantly.

Declaring an Enum

enum declares a type whose values are a fixed set of named variants:

enum Status {
    ready,
    failed,
}

Now Status is a type with exactly two values: Status.ready and Status.failed. Nothing else can be a Status.

The grammar is intentionally tiny:

  • enum Name { opens the declaration.
  • Each line lists one variant name, comma-separated.
  • } closes it.

No payloads, no discriminator values, no derived methods - that's what keeps enum "the small sum type" in Zero.

Using an Enum

You name a variant by qualifying it with the enum type:

let state: Status = Status.ready

The annotation : Status is optional once the right-hand side pins the type; in most cases you can write:

let state = Status.ready

and the compiler will infer the type to be Status.

Comparing Enum Values

Two enum values are equal when they're the same variant:

if state == Status.ready {
    check world.out.write("ready\n")
} else {
    check world.out.write("not ready\n")
}

That's the simplest way to branch on an enum. For exhaustive analysis - handling every variant explicitly - reach for match:

match state {
    .ready  => { check world.out.write("ready\n") }
    .failed => { check world.out.write("failed\n") }
}

The advantages of match over if/else if show up when you add a third variant later. The compiler will tell you every match that's missing the new case; an if/else chain will silently fall through to its default branch.

Choice and match covers match in more detail. It works for both enum and choice.

A Worked Example

The official Zero sample puts enum and choice side by side in the same file:

Status doesn't do anything in this snippet - it's there to show the contrast. A choice variant binds a payload (value, message) when matched; an enum variant binds nothing because there's nothing to bind.

Enum vs. Choice: A Quick Decision Tree

A short rule:

  • The variants are just labels → enum.
  • The variants have to carry data → choice.

If you're modeling lifecycle states and you eventually need to attach an error message to the "failed" state, switch the type from enum to choice. The variants get a payload type each, and downstream match arms gain a binding for that payload. It's a refactor the compiler walks you through.

Concretely:

// Before - enum, no payloads
enum Status {
    ready,
    failed,
}

// After - choice with payloads on each variant
choice Status {
    ready: Void,
    failed: String,
}

The variants whose payload is Void are just labels in choice form. You can use enum and choice for the same logical states; pick enum when you genuinely don't need data attached.

Use Cases

Some everyday examples where enum is the right answer:

  • Lifecycle without metadata. Loading, Ready, Empty - pure states, no payloads.
  • Modes. Read, Write, Append for a file open mode.
  • Direction. North, South, East, West.
  • Log level. Trace, Debug, Info, Warn, Error. (You might later add a message, at which point you'd switch to choice.)
  • Day of the week. A canonical example.

Whenever you'd otherwise reach for a magic integer constant (0 = pending, 1 = active, 2 = done), an enum is almost always clearer.

Style Notes

  • Lowercase variant names match Zero's style for identifiers across the rest of the language.
  • A trailing comma after the last variant is fine (and recommended for diff-friendliness - adding a new variant doesn't churn the previous line).
  • Keep enum lists small. If you have a dozen variants and many of them want payloads, you might be looking at a choice - or a redesign - rather than a bigger enum.

Next: Choice and Match

The natural next step is the richer cousin: choice and match - Zero's tagged-union type and the pattern-matching construct that goes with it.

Frequently Asked Questions

What is an enum in Zero?

An enum declares a type whose values are one of a fixed set of named variants - labels without any extra payload. Example: enum Status { ready, failed }. A value of type Status is exactly one of Status.ready or Status.failed, and the compiler enforces that.

How is enum different from choice?

An enum's variants are plain labels - they don't carry data. A choice is a tagged union - each variant has an associated payload type, like choice Result { ok: i32, err: String }. Use enum when you only need to distinguish cases by name; use choice when each case carries extra information.

How do you check which enum variant a value has?

Compare the value to the variant: if status == Status.ready { ... }. For exhaustive branching across all variants, use match - the compiler will warn if you miss a variant, which is the main reason to prefer match over if/else if chains when the value is a sum type.

Can enum variants have associated values in Zero?

No - that's what choice is for. enum is deliberately the minimal sum type: each variant is just a label. If you need to attach an i32 or a String to one of the variants, you've outgrown enum and want a choice.

When should you use an enum in Zero?

Use enum when a value has to be exactly one of a small, named set of states and those states don't carry extra data. Examples: day of the week, traffic-light color, lifecycle state with no metadata, a logging level. If you find yourself wanting to attach data to one of the variants, switch to choice.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED