Skip to content

JuliaDynamics/DynamicSumTypes.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

DynamicSumTypes.jl

CI codecov Aqua QA DOI

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 much more simple and 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 DynamicSumTypes

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

Benchmark code
using BenchmarkTools
using DynamicSumTypes
       
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)
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.

These benchmarks have been run on Julia 1.11

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.