# \[ANN\] LightSumTypes.jl v4

**URL:** https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741
**Category:** Package Announcements
**Tags:** package, announcement, performance, macros, type-stability
**Created:** [July 7, 2024, 11:40pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741 "2024-07-07T23:40:50Z")
**Posts on this page:** 14
**Page:** 2

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [July 12, 2024, 6:09am UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/21 "2024-07-12T06:09:23Z")

</div>

Would the version with sumtype be faster?

It just stuck me that union of subtypes is different than its supertype. Nice gotcha, Thanks

---

<div class="post-metadata">

### Author: ![abraemer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abraemer/32/51403_2.png) [@abraemer](https://discourse.julialang.org/u/abraemer)
#### Post date: [July 12, 2024, 6:41am UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/22 "2024-07-12T06:41:13Z")

</div>

> [@Tomas\_Pevny](#):
>
> Would the version with sumtype be faster?

IIUC, that is the whole point of sumtype 🙂 Essentially you change from having to do a dynamic dispatch on every access to a much cheaper `if/else` on the type - a “dynamic dispatch”-lite if you wish. This `if/else` is cheaper because there are only limited and fixed options whereas the full dynamics dispatch checks for all applicable methods, then figures out the most specific one (or errors if there is no single one) and then dispatches to that.

---

<div class="post-metadata">

### Author: ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)
#### Post date: [July 12, 2024, 1:40pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/23 "2024-07-12T13:40:22Z")

</div>

With Julia \<=1.10, I’m sure it will, probably around an order of magnitude. With Julia \>=1.11 I think it will around 1.5-2x faster in many cases with concrete subtypes, this is what I see on some realistic benchmarks on nightly, see [Performance Tips · Agents.jl](https://juliadynamics.github.io/Agents.jl/previews/PR1055/performance_tips/#sum_vs_union). This is because as I said in another comment Julia now seems to be able to actually use Union-splitting effectively fortunately!

Secondly, a `@sumtype` should be much faster to compile. This is something important I think because when you have a lot of types you will have big compile times with a normal `Union`.

---

<div class="post-metadata">

### Author: ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)
#### Post date: [July 13, 2024, 8:11am UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/24 "2024-07-13T08:11:51Z")

</div>

cc @jameson this is the discussion I mentioned regarding the performance of `Union` of composite types or a special macro that composes `if` clauses instead of formal dynamic dispatch.

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [July 13, 2024, 12:14pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/25 "2024-07-13T12:14:49Z")

</div>

Yes, that makes sense. I think we have fixed limits on how many call targets it will consider expanding, and the limit is something very small, even though the limit is probably not necessary in this case and could be removed from the compiler. That change should allow the compiler to automatically generate this if/else nest. Generating it manually is also good, but just perhaps annoying to need to specify explicitly when writing the code.

---

<div class="post-metadata">

### Author: ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)
#### Post date: [July 19, 2024, 1:08am UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/26 "2024-07-19T01:08:04Z")

</div>

thanks @jameson for your comment, but I’m not really sure I understand what “call targets” means, does it mean that Julia can give up on doing union-splitting in some calls to a function which would require that to avoid a dynamic dispatch? I see that in Julia 1.11 it doesn’t give up so easily though, I didn’t find a clear case where this happens. Also, apart from runtime performance, I see also compile time performance increasing by wrapping the `Union` when it is not necessary to compile a different function for each subtype, is this something also possible to emulate in Julia Base?

---

<div class="post-metadata">

### Author: ![jameson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jameson/32/23_2.png) [@jameson](https://discourse.julialang.org/u/jameson)
#### Post date: [July 19, 2024, 9:44pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/27 "2024-07-19T21:44:04Z")

</div>

Yes, there is some threshold values that it uses to evaluate whether the dispatch optimization seems profitable or accidental. It might be interesting to know why the compile time is significantly faster, though I suspect some of it is because the explicit struct wrapper does make the job much simpler for inference, which would otherwise need to decide if that Union was profitable or accidental much more frequently. Emulating that with a typealias to some variant on `Some{Union{...}}` is generally expected if the user wants to avoid the compiler making those heuristic evaluations and specifying those explicitly instead.

---

<div class="post-metadata">

### Author: ![jlapeyre](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlapeyre/32/4514_2.png) [@jlapeyre](https://discourse.julialang.org/u/jlapeyre)
#### Post date: [August 19, 2024, 10:53pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/28 "2024-08-19T22:53:18Z")

</div>

It looks like this is not supported:

```julia
struct Some{T}
   val::T
end

struct None end

@sumtype Option{T}(None, Some{T})

```

Nor are parameterized sumtypes supported in general.

There are a lot of use cases that require parameterized sum types.

However, the code is really simple, and it looks like it would be rather easy to add support.

---

<div class="post-metadata">

### Author: ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)
#### Post date: [August 20, 2024, 1:07am UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/29 "2024-08-20T01:07:40Z")

</div>

yes, this would actually be really useful. I thought (badly) that

```julia
@sumtype Option(None, Some{Some_Concrete_T})

```

would suffice…but obviously it does not because you can’t dispatch on the parameters of the sumtype itself because there is no parameter 😅

So…I already implemented it. Hopefully I didn’t get something wrong: [Make it possible to use parameters in sumtype by Tortar · Pull Request #105 · JuliaDynamics/DynamicSumTypes.jl · GitHub](https://github.com/JuliaDynamics/DynamicSumTypes.jl/pull/105)

---

<div class="post-metadata">

### Author: ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)
#### Post date: [September 10, 2024, 9:49pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/30 "2024-09-10T21:49:22Z")

</div>

Hi all!

I released a new version (4.0.0) of the package which has a slightly different name, because the old one was a bit confusing to some people, and I agree that it was a misnomer. Now the library is called LightSumTypes.jl. “Light” seems better than “Dynamic” for what the library does.

Also, not too much time ago I pushed a change in the code generation and now LightSumTypes.jl is on par with Moshi.jl both on 1.10 and 1.11 in the performance of the micro-benchmark used there: see [Benchmarks | Moshi](https://rogerluo.dev/Moshi.jl/data/benchmark/) and for those who would like to use pattern matching on sumtypes I verified that the `@match` macro in Moshi.jl can be used for pattern matching without any performance drop as far as I can tell:

```julia
julia> using LightSumTypes

julia> using Moshi.Match: @match

julia> struct A end

julia> struct B x::Int end

julia> @sumtype S(A, B)

julia> s = S(B(4))
S(B(4))

julia> @match variant(s) begin
           A() => 1
           B(x) => x
       end
4

```

---

<div class="post-metadata">

### Author: ![Tortar](https://avatars.discourse-cdn.com/v4/letter/t/6bbea6/32.png) [@Tortar](https://discourse.julialang.org/u/Tortar)
#### Post date: [March 24, 2025, 4:14pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/31 "2025-03-24T16:14:32Z")

</div>

Thanks to @lkdvos now the package supports recursive sumtypes in 5.1!

```julia
julia> using LightSumTypes

julia> struct A end

julia> @sumtype B(B, A)

julia> B∘A()
B ∘ A()

julia> B∘B∘A()
B ∘ B ∘ A()

```

---

<div class="post-metadata">

### Author: ![cjdoris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cjdoris/32/213133_2.png) [@cjdoris](https://discourse.julialang.org/u/cjdoris)
#### Post date: [March 24, 2025, 6:15pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/32 "2025-03-24T18:15:04Z")

</div>

> [@Tortar](#):
>
> now the package supports recursive sumtypes

What’s the use-case for this?

---

<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: [March 24, 2025, 7:44pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/33 "2025-03-24T19:44:00Z")

</div>

The most classic example would be a linked list, though I’m not sure this change is able to accomodate that.

The SumTypes.jl version would be

```julia
@sum_type List{A} begin 
    Nil
    Cons{A}(::A, ::List) 
end
Cons(x::A, y::List{Uninit}) where {A} = Cons(x, List{A}(y))

List(first, rest...) = Cons(first, List(rest...))
List() = Nil

```

and then you have

```julia
julia> List(1, 2, 3, 4, 5)
Cons(1, Cons(2, Cons(3, Cons(4, Cons(5, Nil::List{Int64})::List{Int64})::List{Int64})::List{Int64})::List{Int64})::List{Int64}

```

and can do stuff like

```julia
julia> Base.sum(l::List{T}) where {T} = @cases l begin
           Cons(head, tail) => head + sum(tail)
           Nil => zero(T)
       end

julia> sum(List(1,2,3,4,5))
15

```

---

<div class="post-metadata">

### Author: ![lkdvos](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lkdvos/32/43091_2.png) [@lkdvos](https://discourse.julialang.org/u/lkdvos)
#### Post date: [March 26, 2025, 1:59pm UTC](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741/34 "2025-03-26T13:59:32Z")

</div>

In my case it is for symbolic algebra, combined with SymbolicUtils.jl : I know they are using Unityper.jl, but I found LightSumTypes.jl is a bit clearer in what is really going on, which helps me for developing.

Something like this:

```julia
@sumtype OperatorExpr{T}(Op,Sum{OperatorExpr{T}},Prod{OperatorExpr{T}})

```

The nested feature I needed was actually more about having nested type parameters, which was failing before.

[Previous page](https://discourse.julialang.org/t/ann-lightsumtypes-jl-v4/116741.md?page=1)
