Module prefixes error in `@cases` even when lack of export would make prefixing desirable
When defining sum types inside modules, arguably it would be desirable to be able to use the module prefix in order to make it clear where they are coming from. However, this currently does not seem to work:
julia> module MySumTypes
using SumTypes
@sum_type MySumType begin
Variant1
Variant2
end
end
Main.MySumTypes
julia> using .MySumTypes, SumTypes
julia> a = MySumTypes.Variant1
Variant1::MySumType
julia> @cases a begin
Variant1 => 1
Variant2 => 2
end
1
julia> @cases a begin
MySumTypes.Variant1 => 1
MySumTypes.Variant2 => 2
end
ERROR: LoadError: Invalid variant MySumTypes.Variant1
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:35
[2] (::SumTypes.var"#69#72"{Expr, Base.RefValue{Any}, Vector{Any}})(::Tuple{Int64, Expr})
@ SumTypes ~/.julia/packages/SumTypes/aO6qd/src/cases.jl:69
[3] foreach(f::SumTypes.var"#69#72"{Expr, Base.RefValue{Any}, Vector{Any}}, itr::Base.Iterators.Enumerate{Vector{Any}})
@ Base ./abstractarray.jl:3187
[4] _cases(to_match::Symbol, block::Expr)
@ SumTypes ~/.julia/packages/SumTypes/aO6qd/src/cases.jl:35
[5] var"@cases"(__source__::LineNumberNode, __module__::Module, to_match::Any, block::Any)
@ SumTypes ~/.julia/packages/SumTypes/aO6qd/src/cases.jl:22
in expression starting at REPL[29]:1
The left hand side of the => in a @cases block is always the symbols associated with a. Namespacing them like this is actually unnecessary.
When you write
@cases a begin
Variant1 => 1
Variant2 => 2
end
here's the macroexpansion:
julia> @macroexpand1 @cases a begin
Variant1 => 1
Variant2 => 2
end
quote
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:115 =#
let var"##data#264" = a
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:116 =#
var"##Typ#268" = (typeof)(var"##data#264")
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:117 =#
(SumTypes.check_sum_type)(var"##Typ#268")
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:118 =#
(SumTypes.assert_exhaustive)(Val{(SumTypes.tags)(var"##Typ#268")}, Val{(:Variant1, :Variant2)})
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:119 =#
var"##unwrapped#270" = (SumTypes.unwrap)(var"##data#264")
#= /home/mason/Dropbox/Julia/SumTypes/src/cases.jl:120 =#
if var"##unwrapped#270" isa (SumTypes.Variant){:Variant1}
#= REPL[13]:2 =#
nothing
1
elseif var"##unwrapped#270" isa (SumTypes.Variant){:Variant2}
#= REPL[13]:3 =#
nothing
2
end
end
end
or a bit more readable:
julia> prettify(ans)
:(let fly = a
penguin = typeof(fly)
check_sum_type(penguin)
assert_exhaustive(Val{tags(penguin)}, Val{(:Variant1, :Variant2)})
locust = unwrap(fly)
if locust isa (SumTypes.Variant){:Variant1}
nothing
1
elseif locust isa (SumTypes.Variant){:Variant2}
nothing
2
end
end)
The point here is that it is only ever checking that the type tag associated with the first argument is :Variant1 or :Variant2, it's not checking the object itself.
This system cannot get confused about namespacing because the lhs is always the one associated with the object you apply @cases to.
Thanks, the point about the internal consistency is well taken. My point was more about human readability. If neither the name of the sum type nor the module appear in the @cases expression, it is somewhat hard to figure out what is happening and where one should look for the definitions. I think a work-around could be that one only uses @cases inside a function that is specialized to the sum type with an explicit type annotation.
If neither the name of the sum type nor the module appear in the
@casesexpression, it is somewhat hard to figure out what is happening and where one should look for the definitions.
Yeah, this is definitely true, but I see it as directly analogous to getproperty, i.e. it's the same problem as when you write x.prop without writing out what the type of x is or where it came from.
I think a work-around could be that one only uses
@casesinside a function that is specialized to the sum type with an explicit type annotation.
Yeah, I think that's generally a good idea when working with @cases, because using it is only valid if you list every single one of the valid variants anyways, so there's not really any such thing as a "generic" function that applies @cases.