# Pattern matching + Enum proposals

**URL:** <https://forum.modular.com/t/pattern-matching-enum-proposals/3473>\
**Category:** Language Design\
**Tags:** discussion\
**Created:** [September 7, 2026, 2:54am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473 "2026-09-07T02:54:29Z")\
**Posts on this page:** 17\
**Page:** 3

<div class="post-metadata">

**Author:** ![dmitry\_salin](https://avatars.discourse-cdn.com/v4/letter/d/ea666f/32.png) [@dmitry\_salin](https://forum.modular.com/u/dmitry_salin)\
**Post date:** [September 15, 2026, 5:25pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/42 "2026-09-15T17:25:57Z")

</div>

The main question is how this can be represented in the type system. I think the term “subtyping” is more appropriate here. This doesn’t necessarily have to conflict with potential inheritance, but yes, some confusion could arise.

---

<div class="post-metadata">

**Author:** ![melodyogonna](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/melodyogonna/32/33_2.png) [@melodyogonna](https://forum.modular.com/u/melodyogonna)\
**Post date:** [September 15, 2026, 8:49pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/43 "2026-09-15T20:49:27Z")

</div>

I’m interested in your idea of how this integrates into Mojo’s semantics. If an `enum` type compiles down to a struct, does `case struct` mean that Mojo has to allow embedding structs into other structs?

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [September 15, 2026, 10:50pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/44 "2026-09-15T22:50:34Z")

</div>

> [@owenhilyard](#):
>
> I think it’s worthwhile to keep the tuple syntax around, since it’s already implemented and for “trivial” enums it does make it much more concise.

Sure, I agree having some kind of sugar could be nice. But I’m not convinced it’s worth making this sugar specific to enums. People don’t declare enums very often, so needing to name each field is not a big deal IMO. If we have sugar, maybe it should be for something that is more general / has broader utility.

Specifically, I am wondering whether Mojo should have a feature akin to Haskell’s `newtype` feature, where you can declare a new nominal type that has the same API as an existing type. For enums, you would then be able to write something like:

```mojo
enum Color:
    # `RGB` is a new nominal type that has the same affordances as Tuple.
    # It is NOT an alias.
    case struct RGB = Tuple[Int32, Int32, Int32]

```

This feature need not be enum-specific:

```mojo
struct UUID = Int128

```

Although `Color.RGB` would still need to contain enough room for a discriminant. So It wouldn’t be “just” a tuple of integers.

If you’re not convinced such a feature is worth adding to Mojo, then I’d say just declare `RGB` to store a tuple as an ordinary field. It’s really not that much boilerplate:

```mojo
enum Color:
    @fieldwise_init
    case struct RGB:
        var value: Tuple[Int32, Int32, Int32]

```

For further concision, hopefully at some point we can replace `@fieldwise_init` with a trait:

```mojo
enum Color:
    case struct RGB(Record):
        var value: Tuple[Int32, Int32, Int32]

```

Regarding:

> [@owenhilyard](#):
>
> I don’t think it’s worthwhile to carry around the discriminant when you have just “lifted” that to comptime with the match statement.

A lot of the opportunities I presented in my last post depend on this discriminant being available. Note that when I talk about the discriminant I don’t necessarily mean a separate tag field, I mean some kind of information embedded in the case struct that allows it to be “upcast” to the enum. (And then pattern matched on etc.)

The subtyping opportunity completely breaks down if the case structs don’t provide this guarantee.

In situations where you want the payload to have no discriminant, you can use the standard nesting trick: have the case struct wrap a separate payload struct.

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [September 15, 2026, 10:55pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/45 "2026-09-15T22:55:04Z")

</div>

> [@ivellapillil](#):
>
> What if the enum declaration defines a trait instead of struct. Then each of the case is implementing the enum trait.

Unless I’m misunderstanding you, this would force every function that takes enum values to become generic. That’s not going to be practical. It would make Mojo enums far more difficult to work with than (e.g.) Rust and Swift enums.

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [September 15, 2026, 11:55pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/46 "2026-09-15T23:55:38Z")

</div>

> [@melodyogonna](#):
>
> I’m interested in your idea of how this integrates into Mojo’s semantics. If an `enum` type compiles down to a struct, does `case struct` mean that Mojo has to allow embedding structs into other structs?

I’m not really sure what you’re asking. The compiler would translate this `enum` feature down to standard structs, and in the MVP the compiler doesn’t need to know anything about subtyping. But I’m not a compiler engineer, so I can’t really speak to the implementation complexity.

I’m not proposing the `case struct` syntax be nestable under ordinary `struct` definitions. But some day, we could explore that kind of direction. (It takes you down the road of predicate typing / refinement typing.)

---

<div class="post-metadata">

**Author:** ![leb-kuchen](https://avatars.discourse-cdn.com/v4/letter/l/a183cd/32.png) [@leb-kuchen](https://forum.modular.com/u/leb-kuchen)\
**Post date:** [September 16, 2026, 12:37am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/47 "2026-09-16T00:37:15Z")

</div>

> <https://github.com/modular/modular/issues/24>
>
> \## Request
> I'm curious what the Mojo team's thoughts are on enum destructuring …(once enums are implemented), especially outside of \`match\` statements.
> 
> \### Motivation
> For a language with enums (a.k.a. "variants" or "sum types"), pattern matching and destructuring is ubiquitous. The traditional way to perform a pattern match is using a \`match\` statement. However, this is too heavy-handed for many use-cases. Accordingly, modern PLs such as Swift and Rust have lightweight syntaxes for destructuring a value within an \`if\` or \`while\` condition. I expect that Mojo will have a similar syntax, so I thought I'd initiate a discussion about it.
> 
> \### Description and Requirements
> 
> Rust has a nice approach (inspired by Swift) for testing whether a value is of a certain variant within an \`if\` or \`while\` condition:
> 
> \`if let Some(x) = iter.next() {...}\`
> 
> The ability to \_chain\_ this destructuring is \[in development\](https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md). It would allow statements such as:
> 
> \`if let Some(x) = iter.next() && let Some(y) = x.foo() {...}\`
> 
> This is a very useful feature, but the syntax is a bit awkward to read. Some Rust contributors proposed an alternative, easier-to-read syntax:
> 
> \`if iter.next() is Some(x) && x.foo() is Some(y) {...}\`
> 
> The challenge with the latter syntax is making it obvious that \`x\` and \`y\` are newly-bound variables. One solution would be to mark them as such. Using Mojo's syntax that might look like:
> 
> \`if iter.next() is Some(var x) and x.foo() is Some(var y) ...\`
> 
> As a bonus, such a syntax could allow users to specify the \_manner\_ in which they want to access the value:
> 
> \`if iter.next() is Some(inout x) and x.foo() is Some(borrowed y) ...\`
> 
> (I'm not sure whether the \_exact\_ keywords that I've used here make sense... I'm just trying to paint a broad picture.)
> 
> Also, having to explicitly declare each variable using \`var\` etc. \*\*would allow \_existing\_ variables to be used within a pattern\*\*, something that many languages don't support:
> 
> \`\`\`
> let x = Leaf
> let tree = Node(3, Leaf, Leaf)
> 
> if tree is Node(3, x, var subtree):
> ...
> \`\`\`
> 
> Another interesting consideration is whether these conditions should be usable as Boolean \_expressions\_. Only expressions that don't introduce variables would be permissible:
> 
> \`let condition = iter.next() is Some(\_)\`
> 
> Finally, Rust also has a nice construct known as \[let-else\](https://doc.rust-lang.org/rust-by-example/flow\_control/let\_else.html), which allows the user to effectively \_assert\_ that a value matches a certain variant. If it doesn't, the current scope must be exited. In Mojo's syntax, this might look like:
> 
> \`iter.next() is Some(var x) else return\`
> 
> I haven't discussed \_unconditional\_ destructuring here (e.g. destructuring a tuple), but I imagine that its design would be consistent with that of conditional destructuring.
> 
> \*\*Side note:\*\* I haven't thought much about whether destructuring operator should be \`is\`, \`==\`, or a new keyword. It's possible that \`is\` isn't the right choice here.
> 
> So, is a syntax such as this in the works? A really practical and intuitive design seems possible.

> <https://github.com/modular/modular/issues/43>
>
> \## Summary
> 
> I propose to consider adding TypeScript-style union types to Mojo …— instead of Rust-style nominal enums. This approach seems like it would work well given that Mojo plans to extend Python, which is a dynamically-typed language. In addition to enabling type declarations for Python libraries, union types would allow Mojo users to avoid the modelling issues associated with nominal enums.
> 
> Union types are strictly more general than nominal enums, and so have the potential to make enums obsolete.
> 
> \*\*Update Jan 2024:\*\* I removed a section that was too speculative, and I posted \[new thoughts\](https://github.com/modularml/mojo/issues/43#issuecomment-1913058371) on how enums/unions might be modelled using dependent types. This approach would supercede most of the design details discussed in this original post.
> 
> \## Motivation
> 
> \### Advantage 1: Specifying precise return types
> 
> Most statically-typed languages that implement type-safe enums use nominal typing. For example, in Rust, to define a function that can return two kinds of error, one must first define (and \_name\_) an enumeration of those error kinds:
> \`\`\`
> enum IOParseError {
> IOError(...),
> ParseError(...),
> }
> \`\`\`
> 
> One can then specify the return type of the error-producing function as follows:
> \`\`\`
> fn parseFile(file: &str) -\> Result\<Success, IOParseError\>
> \`\`\`
> 
> This approach doesn't scale when different functions can return different combinations of errors. For example, maybe we have:
> 
> \- The function \`parseFile\` (above) that produces either an \`IOError\` or a \`ParseError\`.
> \- A function \`readFile\` that only produces an \`IOError\`.
> \- A function \`parseString\` that only produces a \`ParseError\`.
> \- A function \`parseAndExecuteFile\` that produces either an \`IOError\`, a \`ParseError\`, or a \`RuntimeError\`.
> \- ...etc.
> 
> The issue here is that each combination of errors requires a different enum to be declared, and each enum needs to have \_unique\_ constructor names. Ultimately, this makes expressing these return types using nominal enums a nightmare. (And this problem isn't unique to error types. There are many other scenarios in which this issue occurs.)
> 
> In contrast, modelling these functions in a dynamically-typed language (such as Python) is trivial. For example, the implementation of \`parseFile\` will simply return whatever error object is appropriate. No up-front declaration is required.
> 
> Because of this flexibility afforded by dynamically-typed languages, retroactively adding a type system to such languages has proved challenging. But in recent years, TypeScript has demonstrated that it \_is\_ possible to statically-type these kinds of programs. TypeScript's trick is to offer \_union\_ types, via a union operator \`|\`. The union operator can be used to directly express a disjunction of types. We can use the union operator to directly enumerate the specific errors that the aforementioned functions can return:
> \`\`\`
> function parseFile(): Result\<Success, IOError | ParseError\> {...}
> function readFile(): Result\<Success, IOError\> {...}
> function parseString(): Result\<Success, ParseError\> {...}
> function parseAndExecuteFile(): Result\<Success, IOError | ParseError | RuntimeError\> {...}
> \`\`\`
> 
> For brevity, I will omit a full description of how TypeScript's union types work. For more information, check out \[the relevant section\](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types) of the TypeScript handbook.
> 
> \### Advantage 2: Static type-checking (and optimization) of Python code
> 
> Given that Python is a dynamically-typed language, Python programmers mix values of different types together in myriad ways. For example, the use of heterogeneous collections is common amongst Python programmers.
> 
> Accordingly, it would be beneficial if Mojo's static semantics could have a richer understanding of Python, so that more Python code is able to "just work" when copy-pasted into Mojo — even if the code is pasted into a statically-checked \`fn\`.
> 
> Notably, Python already supports the use of union types as part of its type annotations feature. However, Python assigns no static semantics to these annotations. In this post, I'm arguing that giving union types a rigorous static semantics will enable Mojo users to construct expressive, statically-typed code that can be compiled very efficiently.
> 
> \## Design
> 
> Developing a precise semantics for union types will require a large amount of work. Thankfully, TypeScript is a helpful piece of prior art. Scala 3 \[has union types\](https://docs.scala-lang.org/scala3/book/types-union.html) as well. But as I explain below, I think we would want to differ in the fine details.
> 
> I'm unable to dedicate the time required to flesh out a \_full\_ design proposal. However, I can skim over some potential questions and challenges that may arise:
> \- If structs \`Foo\` and \`Bar\` have a common method (not a trait method), should a person with access to a value of type \`Foo | Bar\` be able to call it? (TypeScript allows this.)
> - My belief is that \*\*no, this shouldn't be allowed.\*\* This utility that this would provide is already provided by traits/protocols. By that I mean: if you need to write a function that accepts either \`Foo\` or \`Bar\` and calls a method that they have in common, then you should put that method in a protocol \`P\`, have \`Foo\` and \`Bar\` implement that protocol, and then have your function accept an instance of \`P\` rather than \`Foo | Bar\`.
> - However, let's assume your function truly needs to accept \`Foo | Bar\`, because you want to be able to pattern match on the argument and handle each struct differently. If \`Foo\` and \`Bar\` implement a common protocol \`P\`, should the methods of that protocol be callable? This time I think the answer is \*\*yes, this should be allowed.\*\* Indeed, I relied upon this behaviour earlier, when I described how union types could obviate "trait objects". In short: it should always be possible to \_upcast\_ a value of type \`Foo | Bar\` to a value of type \`P\`.
> - A similar rule would apply if \`Foo\` and \`Bar\` were protocols (rather than structs). A common method should only be callable on \`Foo | Bar\` if the method belongs to a protocol that both \`Foo\` and \`Bar\` inherit from. (This is assuming Mojo supports protocol inheritance.)
> - \*\*Rationale:\*\*
> - If two structs (or two protocols) have field names or method names in common, that doesn't necessarily mean that those fields or methods \_mean the same thing\_. The overlap could be merely coincidental. (Indeed, the \_type signatures\_ of the fields/methods could be drastically different.) In contrast, if two structs share a common protocol, then the two implementations of that protocol \_do\_ mean the same thing. (Because the purpose of a protocol is to \_represent\_ a particular meaning.) Thus, it makes sense that the protocol can be invoked.
> \- Is it possible to compile union types as efficiently as nominal enums?
> - I suspect that the answer is "yes", at least in cases where union types are used in the same manner as enums. For example, if one defines the union type \`type Color = Red | Green | Blue\` (where \`Red\`, \`Green\`, and \`Blue\` are pre-defined struct types with no fields), there is no reason that the compiler can't encode them via the integers \`0\`, \`1\` and \`2\` when storing them in variables of type \`Color\` and when passing them as arguments of type \`Color\`. A difficulty arises when the same structs are used in \_multiple\_ enums, e.g. \`type TrafficLight = Red | Yellow | Green\`. When these two types "interact" (e.g. a \`Color\` value is used as a \`TrafficLight\` argument), \_re-mapping\_ of the tag values may need to occur, e.g. \`Green\` gets mapped from \`1\` to \`2\`. Alternatively, the compiler can avoid the need for such re-mappings by monomorphizing function calls. For example, given a function with an argument of type \`TrafficLight\`, the compiler could generate a "special" version of the function where the tag of each \`TrafficLight\` color is expected to match the tag for the respective \`Color\` color (i.e. \`Green\` is encoded as \`1\`). Call sites that provide a \`Color\` as an argument will invoke this version of the function.
> \- If the elements of a type union are structs, rather than enum-style "variants" (known as \_data constructors\_ in FP), how would pattern-matching and destructuring work?
> - The best approach is probably to pattern-match directly on structs. No notion of a "constructor" is required. This is actually wonderfully consistent with Python: in Python, pattern matching is performed \[directly on objects\](https://peps.python.org/pep-0636/#adding-a-ui-matching-objects).
> - But how would pattern matching on structs work? Not all structs are "plain old data": many \`\_\_init\_\_\` methods are not invertible. Thus, we would probably need a keyword or decorator to designate that a struct is destructurable. The \`@value\` decorator that Mojo offers could be used for this. In fact, Python's \`@dataclass\` decorator \[already works this way\](https://peps.python.org/pep-0636/#matching-positional-attributes) for classes. A more general approach would be to allow structs to define their own destructuring behaviour — analogous to how \`\_\_getitem\_\_\` works, but for destructuring rather than indexing.
> - Structs that are \_not\_ destructurable can still be used in pattern-matching, as long as no attempt is made to destructure them. So instead of writing \`if x is Foo(a, b, c)...\` (a hypothetical syntax for destructuring \`x\`), one would write \`x is Foo\`, which merely tests whether \`x\` is an instance of the struct \`Foo\`. This test would be implemented in the same efficient manner as enums, as discussed earlier.
> \- Union types are typically understood to work like \_sets\_, meaning that duplicate occurrences of a type are eliminated. For example, \`None | None = None\`. How should unification be treated?
> - As a case study, one might try to define the type alias \`Optional\[T\]\` as follows:
> \`type Optional\[T\] = T | None\`
> However, this definition suffers from the issue (?) that \`Optional\[None\]\` is just \`None\`, and that \`Optional\[Optional\[Int\]\]\` is just \`Optional\[Int\]\`. This seems hard to reason about!
> - The "nuclear option" to addressing this issue would be to treat type expressions whose alternatives are able to unify — i.e. "merge together" — as ill-constructed. So the above type alias would be disallowed, and users would have to write a definition like \`type Optional\[T\] = Some\[T\] | None\` instead (where \`Some\` is a pre-defined struct). It seems reasonable to impose this restriction, given that it remains strictly more expressive than traditional enums.
> - Possibly, the only thing that needs to be restricted is the unification of a \_type parameter\_ with something. \`None | None\` doesn't seem so nefarious — and such unification will obviously happen during type inference.
> 
> This is probably enough detail for the moment. What does the Mojo team think of this approach?

> <https://github.com/modular/modular/issues/1345>
>
> \### Review Mojo's priorities
> 
> \- \[X\] I have read the \[roadmap and priorities\](htt…ps://docs.modular.com/mojo/roadmap.html#overall-priorities) and I believe this request falls within the priorities.
> 
> \### What is your request?
> 
> Adding an if let statement in Mojo
> 
> 
> \### What is your motivation for this change?
> 
> Make your mojo better
> 
> \### Any other details?
> 
> \_No response\_

As a reference of old proposed ideas, mainly related to syntax.

The result type would be the next interesting test for enums.

> <https://github.com/modular/modular/issues/2748>
>
> \### Review Mojo's priorities
> 
> \- \[X\] I have read the \[roadmap and priorities\](h…ttps://docs.modular.com/mojo/roadmap.html#overall-priorities) and I believe this request falls within the priorities.
> 
> \### What is your request?
> 
> Result is basically a copy paste of Optional just with an error attribute.
> 
> """Defines Result, a type modeling a value which may or may not be present.
> With an Error in the case of failure.
> 
> Result values can be thought of as a type-safe nullable pattern.
> Your value can take on a value or \`None\`, and you need to check
> and explicitly extract the value to get it out.
> \`\`\`mojo
> from collections import Result
> var a = Result(1)
> var b = Result\[Int\]()
> if a:
> print(a.value()\[\]) # prints 1
> if b: # bool(b) is False, so no print
> print(b.value()\[\])
> var c = a.or\_else(2)
> var d = b.or\_else(2)
> print(c) # prints 1
> print(d) # prints 2
> \`\`\`
> 
> And if more information about the returned Error is wanted it is available.
> \`\`\`mojo
> from collections import Result
> var a = Result(1)
> var b = Result\[Int\](err=Error("something went wrong"))
> var c = Result\[Int\](err=Error("error 1"))
> var d = Result\[Int\](err=Error("error 2"))
> if a:
> print(a.err) # prints ""
> if not b:
> print(b.err) # prints "something went wrong"
> 
> if c.err:
> print("c had an error")
> 
> \# TODO: pattern matching
> if d.err == "error 1":
> print("d had error 1")
> elif d.err == "error 2":
> print("d had error 2")
> \`\`\`
> 
> A Result with an Error cal also be retuned early:
> 
> \`\`\`mojo
> fn func\_that\_can\_err\[A: CollectionElement\]() -\> Result\[A\]:
> ...
> 
> fn return\_early\_if\_err\[T: CollectionElement, A: CollectionElement\]() -\> Result\[T\]:
> var result: Result\[A\] = func\_that\_can\_err\[A\]()
> if not result:
> # the internal err gets transferred to a Result\[T\]
> return result
> # its also possible to do:
> # return None, Error("func\_that\_can\_err failed")
> var val = result.value()
> var final\_result: T
> ...
> return final\_result
> \`\`\`
> 
> 
> \### What is your motivation for this change?
> 
> Better Error handling than raising and giving people tools to write nicer code
> 
> \### Any other details?
> 
> I skimmed over the Error struct's code and it seems it can be done replacing its fields with just a StringLiteral (?)

---

<div class="post-metadata">

**Author:** ![leb-kuchen](https://avatars.discourse-cdn.com/v4/letter/l/a183cd/32.png) [@leb-kuchen](https://forum.modular.com/u/leb-kuchen)\
**Post date:** [September 16, 2026, 10:53am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/48 "2026-09-16T10:53:31Z")

</div>

I think there are multiple aspects to consider.  
First, how common are Tuple cases, that they would need special cased syntax.  
Consider the following preferred syntax:

```auto
enum UpTo3Tuple:
    case a: Int
    case b: Tuple[Int, Int]
    case c: Tuple[Int, Int, Int]

```

Now if we compare this with the first proposed brain dump of Chris ( which basically translates to, let’s copy everything from Swift ), I don’t really see a syntactic advantage here:

```auto
enum UpTo3Tuple:
    case a(Int)
    case b(Int, Int)
    case c(Int, Int, Int)

```

So now the syntax or type narrowing discussion.  
For match the syntax is settled because of the python precedent.  
This now leaves us with if let (or if var) and alternatives.  
I will intentionally ignore compiler complexity for now.  
There are 2 decisions to make, we decide the compiler complexity is to high for now and leave the door open for later or we decide that the compiler complexity would be too high in general.  
This leaves us with `if var PAT = EXPR` and `if EXPR is/as PAT`. There is a lot of room to bikeshed here, a discussion I would not participate in.

EDIT: I also have some concerns regarding the case keyword. I would prefer var, since this would leave the door open for demoting pointer with ref, imm and mut as sugar.

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [October 15, 2026, 1:10am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/49 "2026-10-15T01:10:04Z")

</div>

Note: [My detailed feedback](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/38) on the design of `enum` hasn’t received any attention from Modular yet.

---

<div class="post-metadata">

**Author:** ![clattner](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/clattner/32/201_2.png) [@clattner](https://forum.modular.com/u/clattner)\
**Post date:** [October 15, 2026, 9:19pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/50 "2026-10-15T21:19:05Z")

</div>

> [@Nick](#):
>
> Alright, I’ve now had enough time to study @clattner’s proposal and formulate a proper response. Here’s my feedback.

Hi Nick, I’m sorry for the delay responding, thank you for the ping.

> [@Nick](#):
>
> My main concern about Chris’s proposal (as currently written) is that it leads us away from being able to treat cases as structs.

Why is this a goal? Your proposal is hard for me to understand, because it starts with an elaborate design, but doesn’t have a motivation section. The motivation is spread through the rest of the document.

There are significant problems that I see with it though, for example:

```auto
var highlight = Color.Red
...
if ...:
    highlight = Color.Blue

```

Would fail to compile, because ‘highlight’ would not infer to being type “Color”, it would infer to being the type of the case. This would be very surprising to users, and attempting to correct this would add special cases and break composability.

> [@Nick](#):
>
> ### Guaranteed compatibility between `Pointer[MyEnum]` and `Pointer[MyEnum.Case]`
> 
> Earlier I mentioned that a case struct is not “just” a payload type, because it also (implicitly) stores a discriminant, and has the size and alignment of the parent enum. Here is where that distinction comes into play.
> 
> ..
> 
> Consider the earlier example where we partitioned colors into RGB and HSL. Let’s imagine we want the partitions to store pointers rather than copies of the colors:
> 
> ```mojo
> var colors: List[Color] = get_colors()
> var rgb_colors: List[Pointer[Color.RGB, _]] = []
> var hsl_colors: List[Pointer[Color.HSL, _]] = []
> 
> ```
> 
> Now let’s imagine we’ve defined a function that takes an immutable pointer (or reference) to a color:
> 
> ```mojo
> def foo(color: Pointer[Color, _]): ...
> 
> ```
> 
> It would be great if we could safely pass a pointer to an RGB or a HSL color to this function, even though they expect a more general Color pointer.

I don’t understand the use-case for this - I personally haven’t experienced this need in the sorts of things I’ve programming in other languages. That said, I also don’t understand how this works. Pointer isn’t automatically covariant on its element type, we’d have to add implicit conversions etc to enable this. That would add a lot of complexity.

> [@Nick](#):
>
> Here’s how we might declare a color variable whose case is restricted to be an RGB or HSL color:
> 
> ```mojo
> var c: Color.RGB | Color.HSL
> 
> ```
> 
> Just as in Python, this “union operator” would not introduce a new class or struct. The type of `c` would be a subtype of `Color`, and its runtime representation would be identical to a `Color`. As in Python, we can then rely on the presence of the discriminant to test which (sub)type the variable holds at runtime:

The design doc mentions anonymous cases and anonymous unions because they came up in previous discussions, but it also lists a number of significant problem they introduce. While they’re appealing, it is far from obvious to me that they justify the complexity needed to solve this. In this case, I can definitely see use-cases for them, e.g. it would be convenient to provide “typed bools” e.g. :

```auto
def set_light(state: .on | .off): ...

```

That said, while this is “handy”, this is “just sugar” for something like:

```auto
enum LightState:
    case on, off
def set_light(state: LightState)

```

I am far from convinced it is worth adding complexity to support this.

Separate from that, the biggest concern I have is the fundamental design. By turning cases into types, you’re making another way to define a new type. Why do we want “another way to define a type” vs using the existing way we have to do this?

I don’t see why:

```auto
enum Color:
    case Red
    case Green
    case Blue

    @fieldwise_init
    case struct RGB:
        var r: Int32
        var g: Int32
        var b: Int32

    @fieldwise_init
    case struct HSL:
        var h: Int32
        var s: Int32
        var l: Int32

```

Is better than:

```auto
@fieldwise_init
struct RGB:
    var r: Int32
    var g: Int32
    var b: Int32

@fieldwise_init
struct HSL:
    var h: Int32
    var s: Int32
    var l: Int32

enum Color:
    case red
    case green
    case blue
    case rgb(RGB)
    case hsl(HSL)

```

The later has much better separation of concerns, composability, and stacks more cleanly on top of all of the other things that you can do with structs. I wouldn’t want to see trait conformances, requirement clauses and all the other decorators on enum cases: I’d much rather have “simple things that compose”.

Thank you for taking the time to write this up - it is an interesting design point and one that could certainly work, but I don’t think it is aligned with what we want Mojo to be.

-Chris

---

<div class="post-metadata">

**Author:** ![owenhilyard](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/owenhilyard/32/41_2.png) [@owenhilyard](https://forum.modular.com/u/owenhilyard)\
**Post date:** [October 15, 2026, 10:25pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/51 "2026-10-15T22:25:09Z")

</div>

> [@clattner](#):
>
> Why is [the ability to treat cases as structs] a goal? Your proposal is hard for me to understand, because it starts with an elaborate design, but doesn’t have a motivation section. The motivation is spread through the rest of the document.

Nick and I both want this, so let me take another stab at motivating it.

Consider an actor system. In this actor system, each kind of actor has a set of messages that may be sent to it. These messages may contain a “payload”, data in addition to the information necessary to identify the message type. This makes the definition of the set of messages that may be sent to a particular actor map extremely well to a sum type. This means that there would likely be something like this:

```mojo
struct Actor0:
    def recv_message(mut self, var message: Actor0Message):
        match message:
            case MessageTypeOne(payload):
                self.handle_message_one(payload)
            ...

```

If we aren’t able to treat the value of a case as a struct, then each message type requires the developer to write a unique struct so that the payload can be passed around as a group. While mildly annoying for a small number of cases, consider something like a “remote control plane” API for MAX, where an actor exposes the NVML API remotely to the cluster head node. That API has 380 functions, and writing out those structs as a human or as an LLM is going to result in a lot of boilerplate, and it will also result in a lot of invocations of decorators and reflection functions. Then at the enum declaration site, there needs to be a big set of highly repetitive “mappings” of case to struct.

While it is clearly technically possible to accomplish this without the feature Nick and I want, it results in what I consider some fairly bad UX due to the amount of repetition and namespace pollution it creates.

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [October 16, 2026, 1:04am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/52 "2026-10-16T01:04:27Z")

</div>

Thank you for the response Chris. I know you are very busy, so I appreciate you making time for this.

> [@clattner](#):
>
> Why is this a goal? Your proposal is hard for me to understand, because it starts with an elaborate design, but doesn’t have a motivation section. The motivation is spread through the rest of the document.

The “ **Opportunities** ” subheading is my motivation section. I should have made this clearer.

> [@clattner](#):
>
> There are significant problems that I see with it though, for example:
> 
> ```python-repl
> var highlight = Color.Red
> 
> if ...:
> highlight = Color.Blue
> 
> ```
> 
> Would fail to compile, because ‘highlight’ would not infer to being type “Color”, it would infer to being the type of the case.

I don’t think this would be a problem. I addressed the concern in the following paragraph from my post:

> [@Nick](#):
>
> Case structs are constructed using a standard constructor call, with the exception being that the return type should (by default) be inferred as the enum’s type:

In line with that, I would treat `Color.Red`, when used as a value, to be inferred as having type `Color`. Yes, this does require the construction of a `case`/`case struct` to have “special treatment”, but AFAIK you can explain this through an appropriate desugaring of the constructor call. It’s not particularly complicated, unless I’m missing something.

> [@clattner](#):
>
> I don’t understand the use-case for [compatability between pointers of different types] - I personally haven’t experienced this need in the sorts of things I’ve programming in other languages.

In OOP languages this feature is basically **subtyping.** So if you have ever used subtyping in OOP languages, I’d say you have experience with (approximately) this feature. I can put together some examples if necessary, but they all boil down to “subtyping can be useful”, when used carefully.

> [@clattner](#):
>
> also don’t understand how this works. Pointer isn’t automatically covariant on its element type, we’d have to add implicit conversions etc to enable this.

Yes Mojo will need either some form of implicit conversions, or support for subtyping. That’s why I’m listing this as an “opportunity” that we can pursue at some point in the (distant) future. If/when Mojo adopts `class`es, we will need to implement the exact same “type compatibility” rules that my `Pointer` example depends upon. This is not a coincidence: What I’m basically saying is that with a suitable design for enums, we can treat enum cases as being subtypes of the enum type. This keeps the language small, because we don’t need to implement a lot of enum-specific language features, we just need to implement some flavour of subtyping or type coercion.

> [@clattner](#):
>
> The design doc mentions anonymous cases and anonymous unions

Yes I did see **anonymous sum types** presented in the design doc. Despite the syntax looking similar, I consider them to be a completely different language feature from the **case unions** that I am presenting. So please do not mistake my case unions concept as a pitch for anonymous sum types. I am not interested in the latter feature; we are in agreement there!

> [@clattner](#):
>
> Why do we want “another way to define a type” vs using the existing way we have to do this?

The purpose of my post was to explain this. Clearly I have not done a very good job of that! All of the “ **Opportunities** ” presented in my post are consequences of my design, and AFAIK most of them are not achievable with your original design I am providing feedback on. I hope this is now a bit more clear.

Owen provides further motivation in his most recent post. ^

---

<div class="post-metadata">

**Author:** ![clattner](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/clattner/32/201_2.png) [@clattner](https://forum.modular.com/u/clattner)\
**Post date:** [October 16, 2026, 2:09am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/53 "2026-10-16T02:09:25Z")

</div>

> [@owenhilyard](#):
>
> Consider an actor system. In this actor system, each kind of actor has a set of messages that may be sent to it.

For this specific example, I would recommend checking out how swift actors work. Instead of “go style” manually written dispatch loops and pattern matching, you just define actor methods and the compiler does the work for you. It is /far/ less boilerplate than that you’re describing, because you get rid of all the match and dispatch logic entirely.

Swift concurrency has lots of weird issues, but this core design has worked well. Here’s a foundational doc I wrote about it back in 2017:

> <https://gist.github.com/lattner/31ed37682ef1576b16bca1432ea9f782#part-2-actors-eliminating-shared-mutable-state>

I haven’t seen many other examples where you want to do this sort of thing, and swift (for example) has had enums with broadly this structure and it hasn’t been a problem for people.

You’re right that there are other usecases that aren’t serviced well by enums, but that’s ok - we can have Variant types and other library defined structures as well.

> [@Nick](#):
>
> > [@clattner](#):
> >
> > I don’t understand the use-case for [compatability between pointers of different types] - I personally haven’t experienced this need in the sorts of things I’ve programming in other languages.
> 
> In OOP languages this feature is basically **subtyping.** So if you have ever used subtyping in OOP languages, I’d say you have experience with (approximately) this feature. I can put together some examples if necessary, but they all boil down to “subtyping can be useful”, when used carefully.

Sure, I understand the broad category of feature space this lives in, but Mojo doesn’t have it outside of traits. If/when it does (eg when we introduce classes) we would have to define this, but we haven’t even done so for traits yet.

-Chris

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [October 16, 2026, 2:28am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/54 "2026-10-16T02:28:13Z")

</div>

> [@clattner](#):
>
> Sure, I understand the broad category of feature space this lives in, but Mojo doesn’t have [subtyping] outside of traits. If/when it does (eg when we introduce classes) we would have to define this, but we haven’t even done so for traits yet.

I agree: Mojo doesn’t need subtyping in the short-term; we can think about it later. But my point is that if we want to leave open the possibility of modelling enum cases as subtypes _some day_, we need to design enums with subtyping in mind _today_, from the outset. This leads to the `case struct` model that Owen and I have presented.

**Note:** “Enum cases as subtypes” is not a risky/unproven concept. It has already been battle-tested in Scala. Since Scala 3 (2021), Scala’s enum cases are subtypes, and work almost exactly as I presented in my post. I actually didn’t know about Scala enums at the time I wrote my post, this is just serendipitous, but it provides evidence that my proposal is sensible and well-defined.

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [October 16, 2026, 2:42am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/55 "2026-10-16T02:42:44Z")

</div>

For example, [here is Scala’s Option type](https://docs.scala-lang.org/scala3/book/types-adts-gadts.html#algebraic-datatypes-adts). The main thing to notice is that `Option.Some` and `Option.None` are types.

You can add methods to Scala cases as follows:

```auto
enum Shape:
  case Circle(radius: Double)
  case Rectangle(width: Double, height: Double)

extension (self: Shape.Circle)
  def diameter(): Double = 2 * self.radius

```

Here’s an example of using `Shape.Circle` as a type:

```auto
val circles: List[Shape.Circle] = List(
  Shape.Circle(radius = 3.0),
  Shape.Circle(radius = 5.0)
)

for c <- circles do
  println(c.diameter())

```

---

<div class="post-metadata">

**Author:** ![Nick](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/nick/32/115_2.png) [@Nick](https://forum.modular.com/u/Nick)\
**Post date:** [October 17, 2026, 1:00am UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/56 "2026-10-17T01:00:00Z")

</div>

@clattner I have a simplified proposal that is actionable immediately, and still preserves the future opportunities that Owen and I care about.

When you wrote your initial PR, you required case fields to be **named.** You later changed your mind. Here’s what you said:

> [@clattner](#):
>
> I think there might be some confusion here, which is my fault. I wrote the proposal like this:
> 
> ```sql
> enum Token:
> case eof
> case identifier(value: String)
> case integer(value: Int)
> case source_range(start: Int, end: Int)
> 
> ```
> 
> But as I think you’re pointing out, tuples don’t have names on them.

In short: **I think the syntax in your initial PR was the right choice!** It’s just the desugaring we need to iron out.

Let’s repaint my `case struct` syntax using your original syntax. For reference, here’s `case struct`:

```auto
enum Color:
    case Red
    case Green
    case Blue

    @fieldwise_init
    case struct RGB:
        var r: Int32
        var g: Int32
        var b: Int32

    @fieldwise_init
    case struct HSL:
        var h: Int32
        var s: Int32
        var l: Int32

```

We can repaint it using your original PR’s syntax as follows:

```auto
enum Color:
    case Red
    case Green
    case Blue
    case RGB(var r: Int32, var g: Int32, var b: Int32)
    case HSL(var h: Int32, var s: Int32, var l: Int32)

```

That’s a lot more concise!

I’ve made one small change to your original syntax: I’ve added `var` in front of each argument. This seems important for a few reasons:

- It matches the syntax of field declarations in structs: we declare fields using `var`, to indicate ownership.
- It reflects the signature of `Color.RGB. __init__ `: the initializer takes ownership of its arguments.
- It leaves open the possibility of supporting trait conformances on cases, e.g. `case Foo(var x: Int)(SomeTrait)`. The presence of `var` in the field list makes it clear to both human readers and Mojo’s parser that the first bracket introduces a field list, not a trait conformance list.

Some day, we might also want to use this `var` syntax as shorthand for declaring structs that have the `@fieldwise_init` decorator. This makes the `case` syntax “less special”:

```auto
struct Point(var x: Int, var y: Int)

var p = Point(0, 0) # fieldwise initializer is available

match p:
   case Point(x=a, y=b): ... # pattern matching is available

```

For the `enum` MVP, this covers all of the _syntax_ that Owen and I need as the foundation for a future “type-based” treatment of enum cases. If you were to ship this syntax (i.e. the syntax of your initial PR!) for the MVP, we leave open the possibility of (some day) pursuing the [opportunities](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/38#p-10061-opportunities-provided-by-this-design-post-mvp-2) I presented in my feedback post, without needing to redesign the syntax and mental model of enums to accommodate it.

For the MVP, I don’t really mind whether each case desugars to its own type or not. You can implement whatever desugaring you already had in mind. My goal is just to have the MVP design be forward-compatible with the opportunities Owen and I presented. The above syntax sets us up for that. All I need is for the components of each case to be “field-like”, i.e. accessible by name, rather than being “Tuple-like”.

---

<div class="post-metadata">

**Author:** ![owenhilyard](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/owenhilyard/32/41_2.png) [@owenhilyard](https://forum.modular.com/u/owenhilyard)\
**Post date:** [October 17, 2026, 6:09pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/58 "2026-10-17T18:09:40Z")

</div>

@anatolvitold Could you please split your proposal off into another thread? As best I can tell you don’t have requests that would change the basic implementation of enums, so this can be discussed separately to avoid clogging up this thread.

---

<div class="post-metadata">

**Author:** ![anatolvitold](https://sea1.discourse-cdn.com/flex001/user_avatar/forum.modular.com/anatolvitold/32/1326_2.png) [@anatolvitold](https://forum.modular.com/u/anatolvitold)\
**Post date:** [October 17, 2026, 6:21pm UTC](https://forum.modular.com/t/pattern-matching-enum-proposals/3473/59 "2026-10-17T18:21:19Z")

</div>

@owenhilyard - Not the basic enums. But I do have requests that would change the implementation of pattern matching to make it compatible with inductive types.

[Previous page](https://forum.modular.com/t/pattern-matching-enum-proposals/3473.md?page=2)
