Have a function field in a structure, using another field in the structure

I remember I could do this in Java, and wondered if there was a way to do it in Julia.

E.g. I have a structure

mutable struct my_struct
    val::Float64
    func::Function
end

where the function will do something with the value. I can do this:

ms1 = my_struct(1.5,v->v+1)
ms2 = my_struct(2.5,v->v+2)

println("Result 1: $(ms1.func(ms1.val))")
println("Result 2: $(ms2.func(ms2.val))")

but is there some clever way of making the function referencing the value, without having to pass it into the argument?

You could pass a closure, i.e. make_my_struct(val, func) = my_struct(val, ()->func(val)), but the more Julian approach would be to have a function like apply(ms::MyStruct) = ms.func(ms.val), then println("Result 1: $(apply(ms1))).

Does that answer your question? FWIW, storing functions in structs is not exactly uncommon, but there’s often better ways of doing things.

2 Likes

You definitely don’t want to store a value in the function itself, the function would be too specific to adapt to different values at once. It’s better to store the function and value independently. You do so with a struct, and you could’ve also done so with other containers like Tuples or Arrays.

If you want to save some typing, @cstjean has the right ideas. A functor could reduce typing a bit further. Closure methods like ()->func(val) are actually currently implemented as a hidden functor struct and method, and in this case we just turn your struct itself into a functor:

mutable struct OneArg
  val
  func::Function
end

(m::OneArg)() = m.func(m.val)
# this is a method, but m is not a method name
# m references the functor instance

ms = OneArg(1.5,v->v+1)
println(ms()) # 2.5

ms.val = 2
ms.func = v->v+2
println(ms()) # 4

It’s best to keep the mutable struct with broad abstract field types (in this case, ::Any and ::Function) so you can mutate an instance without running into type conversion errors.

If you end up using an immutable struct and you’re only using a few types of functions and arguments consistently, it might be worth making sure your fields have concrete types

struct ImmutableOneArg{ValType, FuncType<:Function}
  val::ValType
  func::FuncType
end
3 Likes

Thanks, both!

I think I have to digest this a bit though, looks non-trivial.

Here’s the (very short) parts of the docs about functors and closures, studying these may help the digestion more than the examples here:

1 Like