I checked many of the topics related to “UndefVarError” already but did not find the answer to my problem.
Here is the example code:
macro IxConfig(expr::Expr)
if (expr.head == Symbol("struct"))
ps = [];
s = :newfield;
q = :($s::Vector{Float64};);
x = [1,2,3,4,5];
# this one works
p = :(this.$s = $x );
# this one throws exceptions: UndefVarError: .* not defined
# p = :(this.$s = $x .* 3);
prepend!(expr.args[3].args, q.args);
push!(ps, p);
splice!(expr.args[3].args[end].args[2].args, 4, ps);
end
return expr;
end
@IxConfig mutable struct Config
updateid::Int;
function Config()
this = new();
updateid = 0;
return this;
end
end
config = Config()
It works with JuliaPro 1.0.1.1 (Julia Version 1.0.1 (2018-09-29)). But if I change the line
p = :(this.$s = $x );
to the following with a simple multiplication
p = :(this.$s = $x .* 3);
It throws exceptions: “UndefVarError: .* not defined”.
it does not work if using “eval” with the generated code
x = @macroexpand @IxConfig mutable struct Config
updateid::Int;
function Config()
this = new();
updateid = 0;
return this;
end
end
eval(x)
config = Config()
But the generated code itself is fine, if I copied it to another file and execute it there (with minor necessary modification of the local variables)
I’m not sure what your are trying to accomplish, but from your examples it seems that things are getting too complicated. There probably is a simpler solution. My advice: let it go for a few days and then you’ll wake up with a “eureka” moment.
Thanks for your suggestions. It is a small sample code of my project running in 0.6.4. Since Julia does not support inheritance and interface, I use many such macros together to implement the similar concepts.
I know exactly where you’re coming from. I have done similar things in another programming language only to discover that it made my code more difficult to debug because of the many levels of indirection I introduced.
It is better to use Julia on its own merits: multiple dispatch and a very strong type system.
I assume you already now this, but Julia uses the syntax
method(object, parm1, etc) # instead of
object.method(parm1, etc)
Thanks. I am totally with Julia. The sample code is just used to save my typings when dynamically defining many similar and/or related types (“struct”), so that the new defined “type/struct” can have all properties and initializations from its super-type through a macro.
It does seem a bit trickier to add a new field to a mutable struct (at runtime) than I thought, but i guess making it too easy could cause other difficulties with typo’s and etc. I guess i’m a little surprised it might require a macro
I was just looking to see if you can define a struct with optional fields or something similar, I figure an easier way would be to use a Dict for one of the fields and treat it as the extensible part
Yes, I was upgrading a project to Julia 1.0. The problem is solved as suggested by “jandehaan”. Here is an example to explain what I was going to do:
abstract type Food end
mutable struct Fruit <: Food
f1::String
f2::String
f3::String
end
mutable struct Apple <: Fruit
f1::String
f2::String
f3::String
a1::String
end
mutable struct Orange <: Fruit
f1::String
f2::String
f3::String
o1::String
end
# if hundreds of such struct is going to be defined, it will need a lot of typing
Then my original code will be reduced as following
abstract type Food end
mutable struct Fruit <: Food
f1::String
f2::String
f3::String
end
@copy_all_properties_and_initial_values_from_supertype mutable struct Apple <: Fruit
a1::String
end
@copy_all_properties_and_initial_values_from_supertype mutable struct Orange <: Fruit
o1::String
end
Then it will save a lot of my time for typing and for future maintenance when the “Fruit” struct needs to be modified some day.
Hope it explains “y4lu”'s questions as well.
The Julian way to solve this is to use composition over inheritance. This has been discussed in this thread.
I am also with Tamas; if I saw a source file with hundreds of such structs (mutable even!), it would strongly suggest to me that there are better ways to do it. (Although I’m sure there are rare exceptions.)
For the example you mention, you could imagine binding the process function to the fruit:
struct Fruit
name::String
process::Function
end
apple = Fruit("Apple", () -> println("apple"))
orange = Fruit("Orange", () -> println("orange"))
another_orange = Fruit("My Other Orange", orange.process)
apple.process() # => "apple"
another_orange.process() # => "orange"
# or:
process(fruit) = fruit.process()
process(apple)
Inheritance/hierarchy could be achieved by having a parent field in the struct.
Thanks for the suggestions. This approach will work great if all Fruit (Apple, Orange, etc) share the same lis of properties/fields, or functions. In my case, “Apple” and “Oragne” will have different properties except the shared ones as defined under “Fruit”, also, the “Apple” and “Orange” will have some special functions as well. I already tried many different implementations before choosing the current one.