Creating custom type of enumerations of symbols

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.

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)

@enum someenumtype A B C
A

No - I want a typed symbol that contains only specific symbols ā€“ not any symbol

A B and C arenā€™t symbols - I.e. :a

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.

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
2 Likes

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

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.

2 Likes

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

1 Like

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

Julia only checks types at runtimeā€¦

3 Likes

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.

1 Like

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.

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.

thatā€™s the best solution Iā€™ve seen yet. Iā€™d accept that as a readable solution.

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)

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.

1 Like

Is this idea still active in discusion? I would like it very much

Hasnā€™t been implemented, no, nor designed really.