Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enum support in base #3080

Closed
StefanKarpinski opened this issue May 11, 2013 · 29 comments
Closed

enum support in base #3080

StefanKarpinski opened this issue May 11, 2013 · 29 comments
Labels
needs decision A decision on this change is needed

Comments

@StefanKarpinski
Copy link
Member

Currently enum.jl lives in extras. Since enums are generally considered useful, we should do a proper design pass on enums and add them to base. Part of #2422. For design inspiration, the PEP might be helpful: http://www.python.org/dev/peps/pep-0435/.

@JeffBezanson
Copy link
Member

The simplicity of the current thing is great. I would only bring up a couple additional possibilities:

  1. Allow specifying that the new enum is a subtype of something, or
  2. Make all enums a subtype of Enum, to share definitions. This requires...

one new feature: a names method to get the list of possible symbols.

Other possible features:

  1. typemin and typemax definitions
  2. Implement the "ordinal" interface of enum+integer, <, etc.
  3. Maybe conversions to/from integer and symbol

@mlubin
Copy link
Member

mlubin commented May 11, 2013

What if you wanted the symbols to be scoped? Something like

module bar
    export bar
    @enum bar a b c
end

(Incidentally that doesn't seem to work: ERROR: @enum not defined. Yes, I included the file.)

@vtjnash
Copy link
Member

vtjnash commented May 11, 2013

enum.jl has a few module scoping issues which I think I fixed in the open pull request #2988

@JeffBezanson
Copy link
Member

It's best if fixes to existing stuff are separated from new functionality that's up for debate.

@andrioni
Copy link
Member

What are the plans for interoperability with C enums? This will useful to #2976.

@pygy
Copy link
Contributor

pygy commented Jan 7, 2014

Cross referencing JuliaGraphics/Cairo.jl#11, as suggested by @timholy.

As pointed in the mailing list, I wrote this as I was discovering the language, so there's a lot of room for improvement.

I can't find the extras directory anymore... What happened to enum.jl?

@pao
Copy link
Member

pao commented Jan 7, 2014

I can't find the extras directory anymore

#2422 is the extras removal issue, which points back here for enum.jl's fate.

@gitfoxi
Copy link
Contributor

gitfoxi commented Jan 7, 2014

I missed enums at first but once I realized I could use any arbitrary :symbol for a tag, token or state machine then I got over it. What's a use-case for enum?

@nalimilan
Copy link
Member

Off the top of my head: checking that you pass a symbol that exists and is supported by the called method, ordering, C compatibility (and maybe type specialization?).

@tknopp
Copy link
Contributor

tknopp commented Jan 7, 2014

@nalimilan raised a very important point. With enums "done right" it should only be possible to pass a valid enum value to a function that expects a certain enum. This would mean that an enum has an own type an is not only an Int. Still, not only for C compatibility, the values of an enum should be convertable to ints and vice versa. Converting an int to an enum should throw an exception if the value is invalid.

Here is how an enum could look like if one integrates it into the language:

enum MyEnum
   ValueA=1
   ValueB=2
end

function test_my_enum(a::MyEnum)
  if a == MyEnum.ValueA
    ...
  end
  if a == MyEnum.ValueB
    ...
  end 
end

Certainly a switch statement would make this cleaner.

@tknopp
Copy link
Contributor

tknopp commented Jan 8, 2014

One workaround I have used for C compatible enums is:

(ValueA, ValueB, ValueC) = Int32[0:2]

When using this in a module one has to export all values explicitly though.

@tknopp
Copy link
Contributor

tknopp commented Jan 16, 2014

One better alternative for C compatible enums seems to be

baremodule MyEnum
    const ValueA = 0
    const ValueB = 1
end

Now one can simply export only MyEnum. Found this in Gtk.jl.

The actual enum implementation should play nicely with the proposed switch statement #5410.

@StefanKarpinski
Copy link
Member Author

Doesn't give type checks, unfortunately, but I like the simplicity.

@tknopp
Copy link
Contributor

tknopp commented Jan 16, 2014

yes, this is not the ultimate solution. I would vote for an buildin language construct for this with the syntax

enum MyEnum
   ValueA=1
   ValueB=2
end

and MyEnum is an own type that subtypes from Enum

@nalimilan
Copy link
Member

See JuliaStats/DataArrays.jl#50 for a use case of enums with PooledDataArrays.

@quinnj
Copy link
Member

quinnj commented Jan 26, 2014

Another use case for this that I'm really looking forward to is representing Timezones as enum values. I think another great extension of having enums would be the ability to use them as type parameters (because they're just Ints, right?) It would be a nice way of allowing an enum value to be shown as the type parameter instead of just a 0,1,2, etc.

@SimonDanisch
Copy link
Contributor

Hi,
I also am quite interested in solid enum support, for clean error and event handling in ModernGL and GLFW.
So far, I'm using an edited version of the enum macro from the examples.

@GenEnums begin
GLENUM
const GL_MAP1_GRID_SEGMENTS          = 0x0DD1
const GL_COMPILE                                    =  0x1300
const GL_SAMPLER_3D                             = 0x8B5F
const GL_QUERY                                        = 0x82E3
const GL_INTENSITY                                  = 0x8049
end

This will create the following type:

immutable GLENUM{T} <: Enum
number::T
name::Symbol
enumdict = enumdict #Generated dictionary with (uint32 => Symbol)
function GLENUM(number::T)
 if !haskey(enumdict, number)
  error("x is not a GLenum")
 end
 new(number, enumdict[number])
end
end

Like this, if I get an GLenum anywhere, I can find out the corresponding name, to print out better error codes.
Also, it evaluates all the consts, so I have them as well!
I guess my use case is a little too specific, but I wanted to present my solution and state, what kind of demands I have to make enums work for ModernGL.
On top of that, it would be nice, to further group enums like this:
GLDebugEnum <: GLErrorEnum <: GLEnum
GLFloatEnums <: GLTypeEnums <: GLEnum
The syntax would be quite easy to extend:

@GenEnums begin
GLEnum begin
    GLErrorEnum begin
        const GL_NO_ERROR                       = 0
        const GL_DEBUG_TYPE_ERROR    = 0x824C
    end
     GLTypeEnum begin
            GLFloatEnum begin
                const GL_FLOAT_MAT2x4        = 0x8B66
                const GL_FLOAT_VEC3            = 0x8B51
        end
    end
end
end

@tknopp
Copy link
Contributor

tknopp commented Jul 7, 2014

It is not clear to me weather the @GenEnums macro generates a new scope for the enum values. This is in my opinion a rather essential thing. The modul approach taken by Gtk.jl solves this aspect quite nicely.

@SimonDanisch
Copy link
Contributor

No it doesn't, as it would be relatively troublesome for OpenGL.
The OpenGL constants are kind of scoped already, because of the GL_ prefix, which seems to be the preferred way in a lot of C-Libraries.
@jayschwa with his GLFW package solved this a little different, as he introduces his own scope and removed the prefix from the variables instead.
I've mixed feelings about this, as I really like to keep low-level interfaces as similar as possible to the C version to give people used to the c-library an easy start, but on the other side, it seems like a sane thing to do.
But you're right, in a lot of cases scoping is very important for enums ;)
Wrapping @GenEnums in a module would exactly do that!

@tknopp
Copy link
Contributor

tknopp commented Jul 7, 2014

While the enum support that is talked about here certainly should play nice with C libraries I find it more important that it is useful in Julia as a language construct. And without scope you have to repeat the enum type name in the actual values (like the GL prefix).
In Gtk.jl we removed all unnecessary prefixes (which can have multiple levels) and this makes it IMHO nicer. And while I see your point with keeping low level stuff similar, the scoping thing is a sane 1 to 1 mapping and would make the interface "fit into Julia" assuming that we would have scoped enums as a standard language construct in Julia.

I think seeing all these different enum implementations it would be really really great to get support for enums in Julia base/core.

@SimonDanisch
Copy link
Contributor

Just to make sure that this is understood correctly, I'm not arguing for
unscoped enums. I just want to present my perspective on enums in the
context of OpenGL.
First, to give one more voice that cries for enums and second to get some
inspirations from you people and ultimately choose the best solution for
the OpenGL package.
I tend to think, that OpenGL with its ~2000 constants which sort of
randomly evolved over 22 years, would impose too many odd constraints on a
base enum type.
But maybe I'm wrong and we can find an enum, to rule them all! :)

So you're suggesting something like GL.DEBUG.TYPE.ERROR, instead of
GL_DEBUG_TYPE_ERROR?
This looks nice and would be preferable, indeed, especially if this would
encode some accessible hierarchy.
But, I'm really not sure, if the work I need to put into this will justify
the gain.
I just quickly scanned through the ~2000 OpenGL constants, and on first
glance, it seems extremely difficult to make a useful hierarchy out of this.
For most of them, I don't even know what they're supposed to be.
There are very weird names in there, encoding their meaning in many
different ways.
It's highly likely, that you'll end up with a crooked hierarchy, or you'll
need to break the 1:1 mapping, which would be an absolute no-go!
And if you have a crooked hierarchy, you loose the advantage of the nested,
scoped enums and the constants don't look like from OpenGL anymore...
So you actually end up loosing both of the advantages, that you tried to
achieve in the first place.

2014-07-07 19:48 GMT+02:00 Tobias Knopp notifications@github.com:

While the enum support that is talked about here certainly should play
nice with C libraries I find it more important that it is useful in Julia
as a language construct. And without scope you have to repeat the enum type
name in the actual values (like the GL prefix).
In Gtk.jl we removed all unnecessary prefixes (which can have multiple
levels) and this makes it IMHO nicer. And while I see your point with
keeping low level stuff similar, the scoping thing is a sane 1 to 1 mapping
and would make the interface "fit into Julia" assuming that we would have
scoped enums as a standard language construct in Julia.

I think seeing all these different enum implementations it would be really
really great to get support for enums in Julia base/core.


Reply to this email directly or view it on GitHub
#3080 (comment).

@tknopp
Copy link
Contributor

tknopp commented Jul 7, 2014

No I think your solution is just fine at this point. What I mean with "multiple levels" in Gtk is for instance GTK_WINDOW_TOPLEVEL which would be written as GtkWindowType.TOPLEVEL. But since the Gtk namespace is already within the package name, there is also a short form WindowType.TOPLEVEL

So a C enum value usually encodes 3 things. A library name, the enum type and the enum value.

In Gtk.jl all these enums are automatically created by the way.

@quinnj
Copy link
Member

quinnj commented Feb 4, 2015

Having advocated for this, I'm taking a stab at implementing enums (based heavily on @vtjnash's work in #2988). I've also been reviewing Python's internal implementation of enum.Enum.
One big design question that hasn't really been addressed is if we would want Enum values to be restricted to the initial enumeration. Python does enforce this and uses a set internally to track. Much of the discussion and Jameson's initial work is based much more simply on a single Enum + @enum macro that doesn't enforce. This would allow something like the following

@enum FRUIT apple=1, orange=2, kiwi=3

f(x::FRUIT) = "hey, i'm a fruit"

f(FRUIT(4)) # valid, even though we didn't define a FRUIT(4)

Thoughts on this?

@eschnett
Copy link
Contributor

eschnett commented Feb 4, 2015

This should be invalid. I like to use enums to ensure all cases are handled, and if there is an easy way to accidentally create invalid enums, this is thwarted.

@elextr
Copy link

elextr commented Feb 5, 2015

Is this valid?

@enum FRUIT apple=1, orange=2, kiwi=4

fruit_salad = FRUIT(6)

even though 6 is not one of the enumeration values?

@catawbasam
Copy link
Contributor

I agree with @eschnett. FRUIT(6) should be invalid.

@elextr
Copy link

elextr commented Feb 5, 2015

So why give them numbers and convert to/from int? Don't leak the implementation :)

The sets of bits passed to/from C functions in ints are different things to enums as @eschnett wants to use them, they shouldn't be the same concept.

PS I'm not advocating for/against, just asking some questions to ensure that they are considered.

@ivarne
Copy link
Member

ivarne commented Feb 5, 2015

The benefit of leaking the implementation is that it gives a direct way to translate things to C (and other languages, or storage systems that use integers for categorical values). It's also a great way to name some magic numbers that is used in calculations (eg. bitmasks, but arguably that could be a different concept from a enum).

I just came to think of Char as a special case of enum, but 1114112 unicode characters might require more infrastructure than what we generally want for a 2-20 element enum.

@quinnj
Copy link
Member

quinnj commented Feb 21, 2015

Closed by #10168

@quinnj quinnj closed this as completed Feb 21, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs decision A decision on this change is needed
Projects
None yet
Development

No branches or pull requests