Define a type on the fly

Meta programming gurus, how can I create a struct from two Symbols?

So say I have two arrays of symbols, and I want to create types (and set their abstract type) from all their possible combinations, it would look kind of (but not quite) like this:

abstract type AbstractType end
for x in [:A, :B], y in [:Y, :Z]
    struct $x$y <: AbstractType end
end

and the result would mean that the following would be defined:

struct AY <: AbstractType end
struct AZ <: AbstractType end
struct BY <: AbstractType end
struct BZ <: AbstractType end

eval a quoted expression.
Symbols can by combined using the Symbol function.

x = :A 
y = :B
ToInheritFrom = :Any

eval(:(struct $(Symbol(x,y)) <: $ToInheritFrom end))

What are you trying to do? There’s quite possibly a better way to go about this.

2 Likes

What are you trying to do? There’s quite possibly a better way to go about this.

I suspect you are right. I have a bunch of factors and these have discrete levels. Like Food: fruit, meat, bread… I want to create methods that dispatch on distinct setups, unique collections of factors-level pairs. For that I need to code the factors and their levels as types. I think…

Consider using named tuples. You can also dispatch on them.

1 Like

This looks very promising indeed. Fits right in. But how would I build a NamedTuple “on the fly”? Say I have a Dict where the keys are to be the names in the NamedTuple and the values the values. How can I construct a NamedTuple out of that…?

ps = Dict(:k1 => :v1, :k2 => :v2)
(ps...) # doesn't work

Never mind, reading the docs helps:

NamedTuple{tuple(keys(ps)...)}(tuple(values(ps)...))
1 Like

You may find

helpful.

Wah, I’m sure. Right now I don’t “get it”, but maybe once I start writing the methods for my named tuples I’ll understand EponymTuples better.

Thanks @Tamas_Papp !

Wait a minute, the dispatch is matched on the actual names and only the types of the values for NamedTuples, not the actual values of the values…

So a function that only accepts a will also accept b, cause the type signatures will be identical for both a and b:

a = (:food = :apple, :container = :box)
b = (:food = :meat,  :container = :can)

Am I missing something? If that is the case then I’ll need to think this over. All my factor names (i.e. the name part of the named tuple) are always the same. It’s the specific level (i.e. the value part of the named tuple) that changes… It’s the level I want to dispatch on.

Possibly, as

  1. a and b above have the same type (check with typeof),
  2. forms like EponymTuples.@eponymargs(a, b::Int) allow you to dispatch on the type of fields.

This package facilitates a programming style I am experimenting with, but is by no means necessary for dispatching on named tuples.

I could switch the names with the values (and ditch the values, which were the factor names before):

julia> a = (apple = nothing, box = nothing)
(apple = nothing, box = nothing)

julia> b = (meat = nothing,  can = nothing)
(meat = nothing, can = nothing)

julia> typeof(a) ≠ typeof(b)
true

But feels like I’m abusing named tuples to get dispatch specifying on values…?

How about creating types Apple() etc and dispatching on combinations of those (if that’s really what you need to do)?

yea, that was my first line of thought, creating tons of singletons, one per level. But I wish there was a macro for that…