UndefVarError: .* not defined


#1

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

Any suggestions? Thanks.


#2

In the Repl with julia 1.0.0, it seems to be fine

> x=[1;1;1]
> j = :(x = $x .* 3)
> eval(j)'
3 3 3

#3

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)

below is the generated code:

mutable struct Config
      newfield::(Main.Vector){Main.Float64}
      #= ... =#
      updateid::Main.Int
      #= ... =#
      function Config()
          #= ... =#
          #925#this = new()
          #= ... =#
          (#925#this).newfield = [1, 2, 3, 4, 5] .* 3
          #= ... =#
          return #925#this
      end
end

#4

I currently don’t have an environment to try it out, but consider adding parentheses:

p = :(this.$s = $(x .* 3));

#5

Yes, the parentheses works. Thanks you!!!


#6

Would it be better as

mutable struct Config
    updateid::Int;
    Config() = new(0);
  end

configA = Config() ##standard
configB = @IxConfig Config() ##with newfield

#7

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


#8

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.


#9

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)

Inheritance is provided thru Julia’s type system.

method_name(object::TypeName) = ...
method_name(object::SuperType) = ...

And you build informal interfaces by being consistent with your method names and writing good documentation such as this.

Enjoy bending your mind towards a new paradigm. Remember, there is no spoon :wink:


#10

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.


#11

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


#12

You cannot add a field to a (mutable) struct.

As Jan suggested, there is probably a different way to do what you’re trying to do.
What exactly are you looking to do?

(And I suggest upgrading to Julia 1.0.)


#13

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


#14

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 

So I use a macro, say

@copy_all_properties_and_initial_values_from_supertype ....

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.


#15

… then probably this is the wrong approach. A Dict (or similar, eg one of its extensions in DataStructures.jl) or a NamedTuple should be better.


#16

A Dict can do the same job, and the functions might need to be “dispatched” like this:

function process(d::Dict{Symbol, Fruit}, t::Symbol) 
      f = d[t];
      t == :Apple & println("apple");
      t == :Orange & println("orange");
end 

For Struct, Julia will take care of that

process(f::Fruit) = println("fruit");
process(a::Apple) = println("apple");
process(o::Orange) = println("orange")

Either way works


#17

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.


#18

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.