Julia: Feature request: getproperty on enum type to access instances

Created on 24 Feb 2019  路  4Comments  路  Source: JuliaLang/julia

This has come up on slack in the past but I couldn't find an issue about it so I'm opening one.

In a package API, when the user can choose among a set of options, some authors prefer enums over symbols. The awkward part of using enums though is that exporting all of them clutters the global namespace a bit too much: it'd be nicer to be able to export just the type and access the instances with getproperty.

Example of desired behavior:

module StatsMakie
    export BarPosition
    @enums BarPosition stack dodge superimpose
end

using StatsMakie
BarPosition.stack

With the extra advantage that the user can discover all options via tab-completion on BarPosition.

At the moment this is not possible, even though there was a suggested workaround (which is along the lines of what StatsMakie currently does):

module StatsMakie
    module BarPosition
        @enums _BarPosition stack dodge superimpose
    end
    export BarPosition
end

Most helpful comment

I'll also add that I'm personally mostly against this, since it prevents using . to access properties of types themselves. For example in the compiler and reflective code we use T.abstract, T.size etc. All such uses would become invalid and would have to be rewritten to use getfield.

All 4 comments

I have some code along these lines that I use in a personal project that I've meant to polish up and publish, since I know it's come up before. It's pretty small right now, works really well for my use-case. Currently, it only supports storing the inner value as an Int64, but I'd actually like to make it take any number (and type) of arguments, similar to Java enums (since we're creating a full struct anyway). Also currently, you have to pass the intervalue value arguments explicitly, so @scopedenum Fruit apple=1 banana=2. Happy to hack on something w/ somebody if they'd like.

module ScopedEnums

using JSON2

export @scopedenum

macro scopedenum(T, args...)
    blk = esc(:(
        module $(Symbol("$(T)Module"))
            using JSON2
            export $T
            struct $T
                value::Int64
            end
            const NAME2VALUE = $(Dict(String(x.args[1])=>Int64(x.args[2]) for x in args))
            $T(str::String) = $T(NAME2VALUE[str])
            const VALUE2NAME = $(Dict(Int64(x.args[2])=>String(x.args[1]) for x in args))
            Base.string(e::$T) = VALUE2NAME[e.value]
            Base.getproperty(::Type{$T}, sym::Symbol) = haskey(NAME2VALUE, String(sym)) ? $T(String(sym)) : getfield($T, sym)
            Base.show(io::IO, e::$T) = print(io, string($T, ".", string(e), " = ", e.value))
            JSON2.read(io::IO, ::Type{$T}) = $T(JSON2.read(io, String))
            JSON2.write(io::IO, x::$T) = JSON2.write(io, string(x))
        end
    ))
    top = Expr(:toplevel, blk)
    push!(top.args, :(using .$(Symbol("$(T)Module"))))
    return top
end

end

Triage thinks deciding this now would be premature, since we might want to do a more extensive redesign of enums in the future.

I'll also add that I'm personally mostly against this, since it prevents using . to access properties of types themselves. For example in the compiler and reflective code we use T.abstract, T.size etc. All such uses would become invalid and would have to be rewritten to use getfield.

We could just forbid enum member names that collide with fieldnames(DataType) (check in the macro during enum creation).

Then we could also remove the global identifiers (stop polluting the global namespace) and therefore permit two different enums to have members with the same name (imo more relevant than a small documented blacklist of forbidden enum member names), would also gain tab-completion for enum members, and code that uses enums would become more readable.

This would be breaking, though.

In terms of ergonomics, python enums / IntFlags are really well-designed (especially the python IntFlags).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

helgee picture helgee  路  3Comments

m-j-w picture m-j-w  路  3Comments

yurivish picture yurivish  路  3Comments

TotalVerb picture TotalVerb  路  3Comments

wilburtownsend picture wilburtownsend  路  3Comments