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 labelsenum.
  • The variants have to carry datachoice.

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