Creating custom type of enumerations of symbols


#1

Is it possible in julia to create a named-type that is an enumeration of symbol values?

for example a type named Mode that constists solely of symbols :a, :b, and c for example.

I tried constructing it with Val and Union but failed with type conversion errors.


#2

Is this the sort of thing you are talking about?

julia> struct fun{a,b,c} end

julia> fun{:a,:b,:c}()
fun{:a,:b,:c}()

julia> typeof(ans)
fun{:a,:b,:c}

julia> f(::fun{a,b,c}) where {a,b,c} = (a,b,c)
f (generic function with 1 method)

julia> f(fun{:a,:b,:c}())
(:a, :b, :c)

#3

@enum someenumtype A B C
A


#4

No - I want a typed symbol that contains only specific symbols – not any symbol


#5

A B and C aren’t symbols - I.e. :a


#6

That is what my example in fact does. fun{:a,:b,:c}() is an instance of the fun{:a,:b,:c} type, which specifically has :a and :b and :c in its type, not any symbols.

julia> typeof(fun{:a,:b,:c}()) == typeof(fun{:x,:y,:z}())
false

This example shows that the types are indeed considered different if the symbols change.


#7

Maybe:

struct ConstrainedSymbol{T}
	a::Symbol

	ConstrainedSymbol(a) = ConstrainedSymbol{Any}(a)
	ConstrainedSymbol{Any}(a) = new{Any}(a)
	ConstrainedSymbol{T}(a) where {T} = a in T ? new{T}(a) : throw("Symbol :$a is not in the supported set of symbols $T.")
end
Base.convert(::Type{Symbol}, s::ConstrainedSymbol) = s.a
Base.Symbol(s::ConstrainedSymbol) = s.a
julia> s = ConstrainedSymbol{(:a, :b)}(:a)
ConstrainedSymbol{(:a, :b)}(:a)

julia> s = ConstrainedSymbol{(:a, :b)}(:b)
ConstrainedSymbol{(:a, :b)}(:b)

julia> s = ConstrainedSymbol{(:a, :b)}(:c)
ERROR: "Symbol :c is not in the supported set of symbols (:a, :b)."
Stacktrace:
 [1] ConstrainedSymbol{(:a, :b)}(::Symbol) at .\REPL[1]:6
 [2] top-level scope at none:0

julia> s = ConstrainedSymbol(:c)
ConstrainedSymbol{Any}(:c)

julia> Symbol(s)
:c

#8

Doesn’t that look exceeding overly complicated to me and a type with fixed symbols???


#9

It seems you have a specific idea in mind. If you can post your attempt, perhaps people can guide you better along your line of thought.


#10

It is not currently possible to define a subtype of Symbol which only includes some symbols, although the idea has been proposed as a move convenient alternative to the current enum implementation.


#11

Thank you. I hope that they do. Without a type-safe symbol subtype the runtime errors are unavoidable.


#12

Julia only checks types at runtime…


#13

As pointed out above, Julia is not type safe in that way anyway. What is your use case? There is probably a better (more Julian) way to do what you’re trying to do. For example, instead of passing around a mode argument that is either :a, :b or :c and then have different functionality depending on that mode, it’d probably be cleaner to pass around functions directly, or structs that contain this functionality.


#14

far enough - For example, assume a function circle(p1, p2, p3, … pn, mode::ModeType) where p1 - pn are parameters and ModeType is ideally just an enumeration of :fill or :outline

the user would call the function circle(p1, p2, …. pn, :fill)

all of the parameters are typed (i.e. p1::P1Type) ensuring that they are checked at runtime. The idea is to limit the mode parameter to the two symbols :fill and :outline - and not for example a misspelled symbol or any other symbol for that matter.


#15

Does this really not work for your application?

julia> struct Mode{s} end

julia> function circle(args,::Mode{s}) where s
             (args,s ∈ [:fill,:outline] ? s : throw(error("wrong Mode, $s")))
         end
circle (generic function with 1 method)

julia> circle("stuff",Mode{:fill}())
("stuff", :fill)

julia> circle("stuff",Mode{:filler}())
ERROR: wrong Mode, filler
Stacktrace:
 [1] circle(::String, ::Mode{:filler}) at ./REPL[20]:1
 [2] top-level scope at none:0

the circle method specializes on the Mode type parameter, :fill. I believe, this should be able to do it.


#16

that’s the best solution I’ve seen yet. I’d accept that as a readable solution.


#17

Here’s another alternative:

julia> @enum Mode fill outline

julia> circle(radius::Float64, mode::Mode) = println("$radius - $mode");

julia> circle(3.14, outline)
3.14 - outline

julia> circle(3.14, dashed)
ERROR: UndefVarError: dashed not defined

julia> instances(Mode)
(fill::Mode = 0, outline::Mode = 1)

#18

yes, but the limitation with this alternative is readability - it’s not instantly clear that “outline” is a symbol and not a variable… that’ why I like the explicit :symbol in the construction.