Hi everyone, I’ve been playing around a bit lately with the idea of ‘sum types’ (aka tagged unions, or enums in Rust circles). These are quite the hotness these days in functional programming and I kept having trouble fully getting my head around them, so I finally just sat down and implemented them in julia, which has clarified things for me a fair amount.

Check it out at SumTypes.jl.

I think at least in a dynamic language like Julia, there’s good reason to be skeptical of any claim that these are an alternative to `Union`

, however I think maybe there are some uses for these things, and at the very least if someone tells you

Julia is a terrible programming language, it doesn’t even have sum types, the greatest language feature ever!

you can just tell them

Big deal, Mason made a sum type package in 55 lines of julia code.

If you really don’t wanna click on the link, here’s an excerpt from the README where I explain a bit about what a sum type is and how they work:

# SumTypes.jl

Sum types, sometimes called ‘tagged unions’ are the type system equivalent of the disjoint union operation (which is *not* a union in the traditional sense). From a category theory perspective, sum types are interesting because they are *dual* to `Tuple`

s (whatever that means).

At the end of the day, a sum type is really just a fancy word for a container that can store data of a few different, pre-declared types and is labelled by how it was instantiated.

Users of statically typed programming languages often prefer Sum types to unions because it makes type checking easier. In a dynamic language like julia, the benefit of these objects is less obvious, but perhaps someone can find a fun use case.

Let’s explore a very fundamental sum type (fundamental in the sense that all other sum types may be derived from it):

```
julia> using SumTypes
julia> @sum_type Either{A, B} begin
Left{A, B}(::A)
Right{A, B}(::B)
end
```

This says that we have a sum type `Either{A, B}`

, and it can hold a value that is either of type `A`

or of type `B`

. `Either`

has two ‘constructors’ which we have called `Left{A,B}`

and `Right{A,B}`

. These exist essentially as a way to have instances of `Either`

carry a record of how they were constructed by being wrapped in dummy structs named `Left`

or `Right`

. Here we construct some instances of `Either`

:

```
julia> Left{Int, Int}(1)
Either{Int64,Int64}(Left{Int64,Int64}(1))
julia> Right{Int, Float64}(1.0)
Either{Int64,Float64}(Right{Int64,Float64}(1.0))
```

Note that unlike `Union{A, B}`

, `A <: Either{A,B}`

is false, and

`Either{A, A}`

is distinct from `A`

.

## Pattern matching on Sum types

Because of the structure of sum types, they lend themselves naturally to things like pattern matching. As such, `SumTypes.jl`

re-exports `MLStyle.@match`

from MLStyle.jl and automatically declares Sum types as MLStyle record types so they can be destructured:

```
julia> @match Left{Int, Int}(1) begin
Either(Left(x)) => x + 1
Either(Right(x)) => x - 1
end
2
julia> @match Right{Int, Int}(1) begin
Either(Left(x)) => x + 1
Either(Right(x)) => x - 1
end
0
```