Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to create types in Julia at runtime?

Tags:

julia

Let's say I want to have a function of the form

abstract RecordType
function CreateRecordType(fields_names::Vector{ASCIIString}, type_name::ASCIIString)
    # magic that creates the type type_name with string fields field_names
end

For example, CreateRecordType(["name","age"], "Person") would create a new type with the following definition:

type Person <: RecordType
    name::ASCIIString
    age::ASCIIString
end

Is this possible to do in Julia?

like image 782
Ben Hamner Avatar asked Nov 22 '14 23:11

Ben Hamner


1 Answers

Approach 1: Parse to get an AST and then eval

Probably the easiest way is to create a string of what you want and then parse it to get an AST, and then eval the AST. You can do any or all within the function. Here's possible simple implementation that does it all

function CreateRecordType(typeName::ASCIIString,fieldNames::Array{ASCIIString,1},fieldTypes::Array{ASCIIString,1})
   s::ASCIIString = "type $typeName <: RecordType\n";

   for i = 1:length(fieldNames)
      s = s*"$(fieldNames[i])::$(fieldTypes[i])\n"
   end
   eval(parse(s*"end"));
   return;
end

Using it...

julia> abstract RecordType;

julia> CreateRecordType("Person",["name","age"],["ASCIIString","Int64"])

julia> bubba = Person("Bubba",2)
Person("Bubba",2)

julia> print(bubba.age)
2

That's probably not as efficient as it could be, instead you can take a look at the AST that parse produces and then create the Expr to make the AST directly and not use parse and not use a string.

Approach 2: Making the AST directly

Here's an alternate form that creates an AST, it's a little more safe because it requires types and symbols rather than opaque strings. This was created by experimenting with the output of various parses as alluded to above. Using Dict instead of 2 arrays is probably better because the field and the type must always be paired.

function createTypeAST(typeName::Symbol,parentType::Type,fields::Dict{Symbol,DataType})
   e = Expr(:type,true)
   push!(e.args,Expr(:<:));
   push!(e.args[2].args,typeName);
   push!(e.args[2].args,parentType);
   push!(e.args,Expr(:block))
   for field in fields
      push!(e.args[3].args,:($(field[1])::$(field[2])));
   end
   return e;
end

2nd form in action

julia> x = createTypeAST(:P1,RecordType,Dict(:a => Int64))
:(type P1<:RecordType
        a::Int64
    end)

julia> eval(x)

julia> y = P1(1)
P1(1)

julia> y.a
1
like image 136
waTeim Avatar answered Oct 03 '22 05:10

waTeim