This is great @mkitti, nice work.
I also wanted to append a couple of favorite examples after trying out an infixed type union on some of my code –
1. A common workflow in data science is to have tables with missing values, which means you might have some table schema that look like this:
julia> typeof(schema((a=[1f0, missing], b=[missing, "hello"], c=[1, missing])))
ScientificTypes.Schema{
(:a, :b, :c),
Tuple{Union{Continuous,Missing},Union{Textual,Missing},Union{Count,Missing}},
Tuple{Union{Float32,Missing},Union{String,Missing},Union{Int64,Missing}},
}
With an infix operator, this gets simplified to:
ScientificTypes.Schema{
(:a, :b, :c),
Tuple{(Continuous|Missing),(Textual|Missing),(Count|Missing)},
Tuple{(Float32|Missing),(String|Missing),(Int64|Missing)},
}
I find it’s easier to parse like this. Not only is it much less verbose, but I feel like it is structurally clearer. Maybe this is because you can clearly identify the different containers from their style – that being (A|B)
for unions and MyType{A,B}
for parametrized types. Whereas before this, squinting at the page, Union{A,B}
looks like it could be another parametrized type that contains both an A
and a B
for some of its fields. But Union
is special in that its type parameters are not all used in a single instantiation – so the visual cue really helps.
2. Setting defaults is easier to read:
Before:
f(; cleanup::Union{Bool,Nothing}=nothing) = ...
which becomes
f(; cleanup::(Bool|Nothing)=nothing) = ...
I like that Bool
appears right next to cleanup
. Before this, the first thing I see is that cleanup
is a Union
, and after parsing one more word I see that it could be a Bool
. But after this, I first read that cleanup
is a Bool
, and the next symbol is “or this other thing”.
But stylistically the choice could be context-dependent. Maybe you would want to very explicitly inform a user that it is a Union
– so you could just use that instead.