How to access other field variables?

I have a composite type which as a field of type Function, how could this function access other fields of struct?

struct Adder
    adds::Vector{Float64}
    f::Function

   Adder(v::AbstractVector{Float64}) = new(v,
                                           (x, i) -> x + adds[i] )
end

julia> obj = Adder([1.0, 2.0] )
Adder([1.0, 2.0], var"#5#6"())

julia> obj.f.([10.0, 20.0], 1:2)
ERROR: UndefVarError: adds not defined

to be concrete, in the above MWE, how could f() access the field variable adds? thanks.

You have to pass the adds object (or other fields) as an argument to f, or capture the fields in a closure via something like (x, i) -> x + v[i] in the constructor.

struct Adder
    adds::Vector{Float64}
    f::Function

    function Adder(v::AbstractVector)
        a = Vector{Float64}(v) # perform type conversion if needed
        new(a, (x, i) -> x + a[i])
    end
end

However, it sounds like you are trying to emulate object-oriented-programming (OOP) syntax in Julia. This will result in non-idiomatic code and poor performance, so as a general rule I would avoid this style. In particular, instead of object.func(args...) syntax as in OOP, in Julia you might do func(object, args...), and define a method of func that takes an parameter of your object type as the first argument.

1 Like

in your modified code, why and how could it give poor performance?
thanks.

In Steven’s particular example, I don’t think there is a performance problem, but in many cases doing things like this you are liable to run into performance of captured variables in closures · Issue #15276 · JuliaLang/julia · GitHub

Because your struct has an abstractly typed field (::Function). So for every call to f the compiler has to figure out the return type at runtime, it can’t inline it, etc. etc.

4 Likes

You could make Adder itself callable:

julia> struct Adder
           adds::Vector{Float64}
       end

julia> (A::Adder)(x,i) = x + A.adds[i]

julia> obj = Adder([1.0, 2.0])
Adder([1.0, 2.0])

julia> obj([10.0, 20.0], 1:2)
2-element Array{Float64,1}:
 11.0
 22.0

That seems more idiomatic to me, and avoids the performance problem pointed out by @stevengj.

2 Likes