Awesome
LightSumTypes.jl
This package allows to combine multiple heterogeneous types in a single one. This helps to write
type-stable code by avoiding Union
performance drawbacks when many types are unionized. Another
aim of this library is to provide a syntax as similar as possible to standard Julia
structs to facilitate its integration within other libraries.
The @sumtype
macro takes inspiration from SumTypes.jl,
but it offers a more idiomatic interface. Working with it is almost like working with Union
types.
Definition
To define a sum type you can just take an arbitrary number of types and enclose them in it like so:
julia> using LightSumTypes
julia> abstract type AbstractS end
julia> struct A{X}
x::X
end
julia> mutable struct B{Y}
y::Y
end
julia> struct C
z::Int
end
julia> @sumtype S{X}(A{X},B{Int},C) <: AbstractS
Construction
Then constructing instances is just a matter of enclosing the type constructed in the predefined sum type:
julia> a = S(A(1))
S{Int64}(A{Int64}(1))
julia> b = S{Int}(B(1))
S{Int64}(B{Int64}(1))
julia> c = S{Int}(C(1))
S{Int64}(C(1))
a different syntax is also provided for convenience:
julia> a = S'.A(1)
S{Int64}(A{Int64}(1))
julia> b = S{Int}'.B(1)
S{Int64}(B{Int64}(1))
julia> c = S{Int}'.C(1)
S{Int64}(C(1))
Access and Mutation
This works like if they were normal Julia types:
julia> a.x
1
julia> b.y = 3
3
Dispatch
For this, you can simply access the variant inside the sum type and then dispatch on it:
julia> f(x::S) = f(variant(x))
julia> f(x::A) = 1
julia> f(x::B) = 2
julia> f(x::C) = 3
julia> f(a)
1
julia> f(b)
2
julia> f(c)
3
Micro-benchmarks
<details> <summary>Benchmark code</summary>using BenchmarkTools
using LightSumTypes
struct A end
struct B end
struct C end
struct D end
struct E end
struct F end
@sumtype S(A, B, C, D, E, F)
f(s::S) = f(variant(s));
f(::A) = 1;
f(::B) = 2;
f(::C) = 3;
f(::D) = 4;
f(::E) = 5;
f(::F) = 6;
vals = rand((A(), B(), C(), D(), E(), F()), 1000);
tuple_manytypes = Tuple(vals);
vec_manytypes = collect(Union{A, B, C, D, E, F}, vals);
iter_manytypes = (x for x in vec_manytypes);
tuple_sumtype = Tuple(S.(vals));
vec_sumtype = S.(vals);
iter_sumtype = (x for x in vec_sumtype)
@benchmark sum($f, $tuple_manytypes)
@benchmark sum($f, $tuple_sumtype)
@benchmark sum($f, $vec_manytypes)
@benchmark sum($f, $vec_sumtype)
@benchmark sum($f, $iter_manytypes)
@benchmark sum($f, $iter_sumtype)
</details>
julia> @benchmark sum($f, $tuple_manytypes)
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
Range (min … max): 81.092 μs … 1.267 ms ┊ GC (min … max): 0.00% … 90.49%
Time (median): 85.791 μs ┊ GC (median): 0.00%
Time (mean ± σ): 87.779 μs ± 18.802 μs ┊ GC (mean ± σ): 0.35% ± 1.67%
▂ ▃▇█▆▆▅▃▂▂▂▁▁ ▂
█████████████████▇▇▇▅▆▅▄▅▅▅▄▄▄▄▄▄▅▄▄▄▄▄▄▄▃▄▅▅▄▃▅▄▅▅▅▄▅▅▄▅▅▅ █
81.1 μs Histogram: log(frequency) by time 130 μs <
Memory estimate: 13.42 KiB, allocs estimate: 859.
julia> @benchmark sum($f, $tuple_sumtype)
BenchmarkTools.Trial: 10000 samples with 107 evaluations.
Range (min … max): 770.514 ns … 4.624 μs ┊ GC (min … max): 0.00% … 0.00%
Time (median): 823.514 ns ┊ GC (median): 0.00%
Time (mean ± σ): 826.188 ns ± 42.968 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
█ ▁ ▂ ▂
▂▁▂▂▂▂▂▂▂▂▂▂▂▂▂▂▇▂█▃██▃█▅█▄▂██▂█▅▃▃▂▂▃▂▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▃
771 ns Histogram: frequency by time 900 ns <
Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark sum($f, $vec_manytypes)
BenchmarkTools.Trial: 10000 samples with 207 evaluations.
Range (min … max): 367.164 ns … 566.816 ns ┊ GC (min … max): 0.00% … 0.00%
Time (median): 389.280 ns ┊ GC (median): 0.00%
Time (mean ± σ): 390.919 ns ± 9.984 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▁ ▇▁ ▃ ▁ █ ▂
▂▂▃▂▁▁▂▁▁▂▂▁▂▂▄█▃██▃█▃▄█▂█▇▃█▅▃█▃▃▃▂▃▂▂▃▂▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂ ▃
367 ns Histogram: frequency by time 424 ns <
Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark sum($f, $vec_sumtype)
BenchmarkTools.Trial: 10000 samples with 254 evaluations.
Range (min … max): 297.016 ns … 464.575 ns ┊ GC (min … max): 0.00% … 0.00%
Time (median): 308.811 ns ┊ GC (median): 0.00%
Time (mean ± σ): 306.702 ns ± 7.518 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▁ ▆█▅ ▅█▆▁▁▅▄ ▂▂ ▁ ▁▄▄▁ ▂▁ ▂
▇██████▇▅▅▄▅▅▄▄▄▃▄▄▅▅▅▆████████▇▆██▆▅▇██▅███████▇██▇▃▄▅▆▅▅▄▃▅ █
297 ns Histogram: log(frequency) by time 326 ns <
Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark sum($f, $iter_manytypes)
BenchmarkTools.Trial: 10000 samples with 10 evaluations.
Range (min … max): 1.323 μs … 3.407 μs ┊ GC (min … max): 0.00% … 0.00%
Time (median): 1.390 μs ┊ GC (median): 0.00%
Time (mean ± σ): 1.389 μs ± 54.987 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▅▄▁▂ ▃█▇▇
▃▄▆████▅▄▇████▆▇▆▅▄▄▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▁▂▁▂▂▂▂▁▁▂▂▁▂▂▂▂▂▂▂▂▂ ▃
1.32 μs Histogram: frequency by time 1.67 μs <
Memory estimate: 0 bytes, allocs estimate: 0.
julia> @benchmark sum($f, $iter_sumtype)
BenchmarkTools.Trial: 10000 samples with 258 evaluations.
Range (min … max): 310.236 ns … 370.112 ns ┊ GC (min … max): 0.00% … 0.00%
Time (median): 318.971 ns ┊ GC (median): 0.00%
Time (mean ± σ): 319.347 ns ± 5.859 ns ┊ GC (mean ± σ): 0.00% ± 0.00%
▁ ▄▇▆▁▃▆█▃ ▃▆▅ ▄▆▄ ▃▆▇▃▁▄▇▇▃▁▂▅▄▁ ▁▂▁ ▁ ▁▁ ▁ ▃
▅█▂▆████████▇███▅███████████████████▇██████████████████▇▅█▆▇▅ █
310 ns Histogram: log(frequency) by time 338 ns <
Memory estimate: 0 bytes, allocs estimate: 0.
<sub>These benchmarks have been run on Julia 1.11</sub>
Contributing
Contributions are welcome! If you encounter any issues, have suggestions for improvements, or would like to add new features, feel free to open an issue or submit a pull request.