# What's the point of algebraic data types (Moshi.jl)

**URL:** https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281
**Category:** General Usage
**Created:** [January 27, 2026, 8:45am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281 "2026-01-27T08:45:13Z")
**Posts on this page:** 20
**Page:** 1

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [January 27, 2026, 8:45am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/1 "2026-01-27T08:45:13Z")

</div>

Symbolics.jl uses Moshi.jl extensively, and it yields obscure types like `SymbolicUtils.BasicSymbolicImpl.var"##Storage#AddMul"{T}`. After reading [Algebraic Data Type - Intro | Moshi](http://rogerluo.dev/Moshi.jl/start/algebra-data-type/), I still don’t get it. The motivating example is that

```julia
messages = [Quit(), Move(10, 20), Write("Hello, World!"), ChangeColor(255, 0, 0)]

```

is type unstable. Then the solution is to create a type `Message` type whose field is typed as `Union{Quit, Move, Write, ChangeColor}`. But I don’t get how that’s so fundamentally different. `[Quit(), Move(10,20), ...]` can also have an eltype that is `Union{Quit, Move, Write, ChangeColor}`. Both cases hope-and-pray that the compiler will introduce union splitting appropriately. What’s the benefit of the “algebraic data type”?

(I get the benefit of `@match` though, but AFAICT it would work just as well if `@data` defined `Message = Union{Quit, Move, Write, ChangeColor}`)

---

<div class="post-metadata">

### Author: ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)
#### Post date: [January 27, 2026, 9:31am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/2 "2026-01-27T09:31:52Z")

</div>

Wrapping a union into a struct is a standard way to achieve type stability, e.g. in list comprehension. Consider this:

```julia-repl
julia> a1 = Union{Int, Float64}[1, 2.0];

julia> a2 = Union{Int, Float64}[1, 2];

julia> typeof([i+1 for i in a1])
Vector{Real} (alias for Array{Real, 1})

julia> typeof([i+1 for i in a2]) # Type of the array has changed!
Vector{Int64} (alias for Array{Int64, 1})

julia> struct MyType # Wrap the union into a struct
           data::Union{Int, Float64}
       end

julia> Base.:+(a::MyType, b) = MyType(a.data + b)

julia> a1_stable = map(MyType, a1)
2-element Vector{MyType}:
 MyType(1)
 MyType(2.0)

julia> a2_stable = map(MyType, a2)
2-element Vector{MyType}:
 MyType(1)
 MyType(2)

julia> typeof([i+1 for i in a1_stable])
Vector{MyType} (alias for Array{MyType, 1})

julia> typeof([i+1 for i in a2_stable]) # Type of the array remains unchanged.
Vector{MyType} (alias for Array{MyType, 1})

```

---

<div class="post-metadata">

### Author: ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)
#### Post date: [January 27, 2026, 9:38am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/3 "2026-01-27T09:38:38Z")

</div>

> [@cstjean](#):
>
> Both cases hope-and-pray that the compiler will introduce union splitting appropriately.

For performance, you’re still at the mercy of the compiler. The purpose is formal type stability in the interest of correctness rather than performance.

P.S. some performance benefit is also possible, since the proliferation of types is slightly tamed, as shown by the array types in my list comprehension example.

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [January 27, 2026, 9:59am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/4 "2026-01-27T09:59:52Z")

</div>

Wrapping a union into a struct allows you to control function barriers, i.e. where dynamic dispatch happens. This has huge performance implications, but which one is better depends on your specifics.

Consider

```julia-auto
const TypeUnion = Union{Int, Float64}
foo(x) = nothing; #imagine real code here
struct Wrapped
    member::TypeUnion
end
unwrapped = TypeUnion[1, 1.0]
wrapped = [Wrapped(1), Wrapped(1.0)]
function bar(a)
    foo(a[1]) #critical dispatch!
end

```

If you call `bar(unwrapped)`, then the `foo` call will be a (potentially slow, depending on union splitting) dynamic dispatch. In exchange, the specialized `foo` implementation will exactly know the types of its inputs, making it faster.

If you call `bar(wrapped)`, then the `foo` call will be a fast static dispatch. But in exchange, the `foo` implementation will have to deal with not knowing the exact type of `x.member`, potentially making it slower and incurring (potentially slow) dynamic dispatch if it calls anything with `x`.

So you have to judge / trade-off:

Will foo just pass through its parameters to other callees, without unwrapping them? Wrap the union into a struct. No function barrier.

Will foo do computations on the union-typed arguments, possibly in a loop? Pass the parameter unwrapped, i.e. push the dynamic dispatch up the call-stack. Standard design principle in julia, cf function barrier to enable type stability.

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [January 27, 2026, 11:14am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/5 "2026-01-27T11:14:51Z")

</div>

This thread needs corrections… 😅

Correction: my thread needs corrections 🤣 I described Unityper. While Julia’s Union optimizations do work similarly at the LLVM level, Moshi.jl does work differently from what I describe except for the pattern matching part. It relies on the Julia union splitting optimizations.

> [@cstjean](#):
>
> Then the solution is to create a type `Message` type whose field is typed as `Union{Quit, Move, Write, ChangeColor}`. But I don’t get how that’s so fundamentally different. `[Quit(), Move(10,20), ...]` can also have an eltype that is `Union{Quit, Move, Write, ChangeColor}`. Both cases hope-and-pray that the compiler will introduce union splitting appropriately. What’s the benefit of the “algebraic data type”?

No, that is a major misunderstanding of what the type is. `Message` is not a type whose field is typed as `Union{Quit, Move, Write, ChangeColor}`. `Message` is a type whose fields are a union of the fields used by non-existent types `{Quit, Move, Write, ChangeColor}`. Those are pseudo-types, or varients. There is only one type: `Message`. It looks like:

```julia-auto
struct Message
  varient::Symbol
  x::Int
  y::Int
  message::String
  r::Float64
  g::Float64
  b::Float64
end

```

Now since the different varients don’t share any fields, this isn’t a great example, but if for example `Message` also had a `::Float64` then what you’d want to do is combine the two in a field and then `getproperty` alias them to save memory. But no matter, it’s a type that is a union of fields. So then effectively:

```julia-auto
Move(x::Int, y::Int) = Message(:Move, x,y, undef, undef, undef, undef)

```

`Move` is just an alias to a type of `Message`. All of them are just messages. So

```julia-auto
messages = [Quit(), Move(10, 20), Write("Hello, World!"), ChangeColor(255, 0, 0)]

```

is type-stable, it’s just `Array{Message}`.

Now when you write functions over an `Array{Message}`, you may want to dispatch over `varient`s, in which case you do explicit `if` statements:

```julia-auto
function myfunction(m::Message)
   if m.varient == :Quit
       # Do something
   elseif m.varient == :Move
      # Do something
    ...
end

```

This is what the pattern matching macros help you do:

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

```

just writing out big if statements.

So then with this in mind:

> [@greatpet](#):
>
> Wrapping a union into a struct is a standard way to achieve type stability, e.g. in list comprehension. Consider this:

This relies on compiler optimization effectively doing this under the hood which relies on certain heuristics being hit. Moshi does not rely on those compiler optimizations at all.

> [@greatpet](#):
>
> For performance, you’re still at the mercy of the compiler. The purpose is formal type stability in the interest of correctness rather than performance.

Nope, you’re not at the mercy of the compiler because there is no union. Everything is statically typed, no dynamic dispatching. You trade dynamic dispatch for static dispatching in terms of matching, where each time you want to do things you need to write things based on varients.

> [@greatpet](#):
>
> P.S. some performance benefit is also possible, since the proliferation of types is slightly tamed, as shown by the array types in my list comprehension example.

It’s not slightly tamed, there is a guarantee that there is exactly one type, and Julia has a guarentee to then not dynamic dispatch.

> [@foobar\_lv2](#):
>
> Wrapping a union into a struct allows you to control function barriers, i.e. where dynamic dispatch happens. This has huge performance implications, but which one is better depends on your specifics.

Yes, but algebraic data types are not wrapping a union into a struct, so this analysis is not related to what’s actually going on with Moshi. The calls with Moshi will not have dynamic dispatch in your example, instead just an if statement that is statically handled because it would be a single struct

```julia-auto
struct Wrapped
   varient::Symbol
   member1::Int
   member2::Float64
end

```

and then you’d branch based on the varient. There is no type-instability for the compiler to handle.

---

<div class="post-metadata">

### Author: ![ericphanson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ericphanson/32/215186_2.png) [@ericphanson](https://discourse.julialang.org/u/ericphanson)
#### Post date: [January 27, 2026, 11:39am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/6 "2026-01-27T11:39:32Z")

</div>

> [@ChrisRackauckas](#):
>
> There is only one type: `Message`. It looks like:
> 
> ```julia-auto
> struct Message
> varient::Symbol
> x::Int
> y::Int
> message::String
> r::Float64
> g::Float64
> b::Float64
> end
> 
> ```

I don’t think that’s true; if you do a `@macroexpand` you can see it is indeed a wrapped union:

```julia
  begin
      struct var"##Storage#Quit"
      end
      struct var"##Storage#Move"
          #= REPL[8]:4 =#
          x::Int64
          #= REPL[8]:5 =#
          y::Int64
      end
      struct var"##Storage#Write"
          #= REPL[8]:8 =#
          var"##field#281"::String
      end
      struct var"##Storage#ChangeColor"
          #= REPL[8]:9 =#
          var"##field#282"::Float64
          #= REPL[8]:9 =#
          var"##field#283"::Float64
          #= REPL[8]:9 =#
          var"##field#284"::Float64
      end
  end
  begin
      #= /Users/eph/.julia/packages/Moshi/UlzGA/src/data/emit/type.jl:23 =#
      struct var"typeof(Message)"
          data::Union{var"##Storage#Quit", var"##Storage#Move", var"##Storage#Write", var"##Storage#ChangeColor"}
      end
      #= /Users/eph/.julia/packages/Moshi/UlzGA/src/data/emit/type.jl:24 =#
      const Type = var"typeof(Message)"
  end

```

This is also the approach of [GitHub - ameligrana/WrappedUnions.jl: Wrap a Union for enhanced type-stability](https://github.com/ameligrana/WrappedUnions.jl). (I think it was true of Unityper, maybe that’s what you’re remembering?)

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [January 27, 2026, 1:30pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/7 "2026-01-27T13:30:39Z")

</div>

> [@ChrisRackauckas](#):
>
> No, that is a major misunderstanding of what the type is. `Message` is not a type whose field is typed as `Union{Quit, Move, Write, ChangeColor}`. `Message` is a type whose fields are a union of the fields used by non-existent types `{Quit, Move, Write, ChangeColor}`. Those are pseudo-types, or varients. There is only one type: `Message`. It looks like:
> 
> ```julia-auto
> struct Message
> varient::Symbol
> x::Int
> y::Int
> message::String
> r::Float64
> g::Float64
> b::Float64
> end
> 
> ```
> 
> Now since the different varients don’t share any fields, this isn’t a great example, but if for example `Message` also had a `::Float64` then what you’d want to do is combine the two in a field and then `getproperty` alias them to save memory. But no matter, it’s a type that is a union of fields. So then effectively:
> 
> ```julia-auto
> Move(x::Int, y::Int) = Message(:Move, x,y, undef, undef, undef, undef)
> 
> ```

This is a misunderstanding of what Moshi is. It absolutely does use unions internally. You appear to be thinking of Unityper.jl which Symbolics used to use, and it did something similar to what you are describing.

* * *

Edit: Oops: @ericphanson beat me to it, sorry for the noise.

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [January 27, 2026, 1:37pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/8 "2026-01-27T13:37:12Z")

</div>

😅 oops

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [January 27, 2026, 3:41pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/9 "2026-01-27T15:41:38Z")

</div>

> [@ChrisRackauckas](#):
>
> Yes, but algebraic data types are not wrapping a union into a struct,

Yeah, if you cannot guarantee that the types going into the union are mutually disjoint, then you need to wrap them again to force the issue (and you need to keep the wrapper package-private), so generically ADT sums in julia must be wrapped-union-of-wrappers, to property deal with the likes of `Some(Some(Some(nothing)))` in a `Maybe`.

Alas, the extra wrapper can introduce extra overhead, both in julia runtime and in notation. Mathematicians abuse notation all the time with \cup / union when they actually mean \dot\cup / \coprod / disjoint union / coproduct, as long as the sets could be proven disjoint by a sufficiently trivial argument.

Opposed to some pure functional programming people, I think this pragmatism (aka sloppiness) is a good thing.

…going the other direction, in terms of language support, is hard! I have yet to see the ADT functional programming people who are so proud of their sum types implement humble union types as pushout / fibred coproduct.

---

<div class="post-metadata">

### Author: ![marteaua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marteaua/32/214927_2.png) [@marteaua](https://discourse.julialang.org/u/marteaua)
#### Post date: [January 27, 2026, 10:21pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/10 "2026-01-27T22:21:24Z")

</div>

I read your first post and thought “wow, I really didn’t understand anything when reading Moshi’s docs, this post should go into it because it’s quite comprehensive”…

… but now I think that it’s beginning should be edited to mentions that this doesn’t describe Moshi (but Unityper apparently)

---

<div class="post-metadata">

### Author: ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)
#### Post date: [January 30, 2026, 8:30am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/11 "2026-01-30T08:30:42Z")

</div>

Thank you for the feedback everyone!

> [@greatpet](#):
>
> For performance, you’re still at the mercy of the compiler. The purpose is formal type stability in the interest of correctness rather than performance.

How is it more correct than typing with `::Message` where `Message = Union{Quit, Move, Write, ChangeColor}`?

> [@foobar\_lv2](#):
>
> Wrapping a union into a struct allows you to control function barriers, i.e. where dynamic dispatch happens. This has huge performance implications, but which one is better depends on your specifics.

That’s an interesting point. Is it any different performance-wise from using `@nospecialize`? Incidentally, is there any package that implements `@match` without the wrapping?

---

<div class="post-metadata">

### Author: ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)
#### Post date: [January 30, 2026, 8:46am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/12 "2026-01-30T08:46:44Z")

</div>

The difference between a Moshi sum type and a Julian union type is that the former wraps the latter. You get the same effect with a normal struct with a union inside. I.e, this is the same as asking what the difference is between U and X below:

```julia
const U = Union{A, B, C, D, E}
struct X
    x::U
end

```

Let’s look at some differences:

1. Suppose you have a function `f()::U`. Now, what is the type of `[f()]`? Is it `Vector{U}`? Nope! It’s going to be a vector of one of the subtypes. However, for `f()::X`, `[g()]` is indeed a `Vector{X}`. This is part of a more general pattern where any operation on an instance of `U` which uses the type of that instance will not use `U`, but rather the more specific, concrete type. If the concrete type is only known at runtime (and it usually is, for the use cases of sum types), then you have type instability.
2. When the compiler sees an `X`, you know that whoever wrote the code explicitly designed `X` to have the five variants `A` to `E`. However, when the compilser sees a `U`, this may have been generated by a process where there is general uncertainty about the type produced. Applying union-splitting to the `X` is ‘safer’, in that you can be reasonably sure that whatever process produced an `X` is probably only going to concern itself with the five subtypes, and so there will not be a combinatorial explosion of types. In contrast, when dealing with a `U`, precisely because of the “type propagation” of point 1, a type explosion is more likely. For this reason, Julia is more likely to apply union-splitting to a union when it’s wrapped in a struct.
3. Suppose you have a process that returns either a `T` or `nothing`. If you return a `Union{T, Nothing}`, then the caller may simply proceed _assuming_ it was a T, e.g. passing it to some function that demands a `T`. That is very convenient, but it can make the user forget that a `nothing` was possible. Using a sum type forces the consumer to remember all the possibilities, because you _can’t_ pass an `X` to a function that requires an `A`.

---

<div class="post-metadata">

### Author: ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)
#### Post date: [January 30, 2026, 9:06am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/13 "2026-01-30T09:06:40Z")

</div>

> and it yields obscure types like `SymbolicUtils.BasicSymbolicImpl.var"##Storage#AddMul"{T}`

I’ve defined a type-alias

```julia
const BasicSymbolic{T} = BasicSymbolicImpl.Type{T}

```

Both for ease of use, and in the hopes that it would print nicely (similar to how `Vector{Int}` prints instead of `Array{Int, 1}`). For reasons I don’t yet know, it refuses to print the type-alias and insists on printing the generated type.

---

<div class="post-metadata">

### Author: ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)
#### Post date: [January 30, 2026, 11:21am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/14 "2026-01-30T11:21:49Z")

</div>

> [@cryptic.ax](#):
>
> `BasicSymbolicImpl.Type{T}`

I don’t think this is a valid use of `Type` in Julia. The `Type` identifier doesn’t belong to any module.

---

<div class="post-metadata">

### Author: ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)
#### Post date: [January 30, 2026, 11:29am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/15 "2026-01-30T11:29:12Z")

</div>

Moshi.jl defines a module for the ADT, and the type of the struct it creates is available as `ADT.Type`. See the tip at the bottom of the “Quick Example” section in [Syntax & Examples | Moshi](https://rogerluo.dev/Moshi.jl/data/syntax/).

---

<div class="post-metadata">

### Author: ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)
#### Post date: [January 30, 2026, 11:29am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/16 "2026-01-30T11:29:54Z")

</div>

Additionally, the “normal” `Type` identifier belongs to the `Core` module

---

<div class="post-metadata">

### Author: ![greatpet](https://avatars.discourse-cdn.com/v4/letter/g/e495f1/32.png) [@greatpet](https://discourse.julialang.org/u/greatpet)
#### Post date: [January 30, 2026, 11:32am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/17 "2026-01-30T11:32:34Z")

</div>

I see, it’s a different `Type`.

Type aliases don’t affect printing directly. You’ll need to manually add new `show` methods.

---

<div class="post-metadata">

### Author: ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)
#### Post date: [January 30, 2026, 11:37am UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/18 "2026-01-30T11:37:37Z")

</div>

`Vector{T}` doesn’t have a custom `show` method, and it prints as the alias. As far as I’m aware, implementing `Base.show(io::IO, ::Type{MyType})` is type-piracy and not recommended.

---

<div class="post-metadata">

### Author: ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)
#### Post date: [January 30, 2026, 12:08pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/19 "2026-01-30T12:08:36Z")

</div>

Type aliases only show up in the printing if the alias is defined in the same module as the type (by design): [Type aliases are not printed as aliases if the aliased type is imported from a different module · Issue #40448 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/40448)

```julia-auto
julia> struct Foo{T} end;

julia> const FooInt = Foo{Int}
FooInt (alias for Foo{Int64})

julia> Foo{Int}
FooInt (alias for Foo{Int64})

julia> module Bars
       struct Bar{T} end
       export Bar
       end
       using .Bars

julia> const BarInt = Bar{Int}
Bar{Int64}

julia> Bar{Int}
Bar{Int64}

```

---

<div class="post-metadata">

### Author: ![cryptic.ax](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cryptic.ax/32/220540_2.png) [@cryptic.ax](https://discourse.julialang.org/u/cryptic.ax)
#### Post date: [January 30, 2026, 12:16pm UTC](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281/20 "2026-01-30T12:16:44Z")

</div>

Ah, that explains it. Thanks! I can probably move the alias definition inside the module with `@eval` shenanigans, then.

[Next page](https://discourse.julialang.org/t/whats-the-point-of-algebraic-data-types-moshi-jl/135281.md?page=2)
