Here’s a macro I’ve thrown together and use in a few projects:
macro scopedenum(T, args...)
defs = Expr(:block)
append!(defs.args, collect(:(const $(x.args[1]) = Enum($(x.args[2]))) for x in args))
names = Dict(x.args[2]=>String(x.args[1]) for x in args)
str2val = Dict(String(x.args[1])=>x.args[2] for x in args)
push!(defs.args, quote
function name(e::Enum)
nms = $names
return nms[e.value]
end
Enum(str::String) = Enum($(str2val)[str])
Base.show(io::IO, e::E) where {E <: Enum} = print(io, "$(Base.datatype_module(E)).$(name(e)) = $(e.value)")
end)
blk = esc(:(module $T; struct Enum{T}; value::T; end; Enum{T}(e::Enum{T}) where {T} = e; $defs; end))
return Expr(:toplevel, blk)
end
The basic usage is
@scopedenum Fruit APPLE=1 PEAR=2 BANANA=3
Fruit.APPLE
Fruit.PEAR
# access enum value
Fruit.APPLE.value
# make an APPLE from string
Fruit.Enum("APPLE")
# restricting type signatures
f(x::Fruit.Enum) = # do stuff w/ x