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.
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
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.
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.
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.
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.
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.