Reputation: 143
In Julia I've defined a type and I need to write some functions that work with the fields of that type. Some of the functions contain complicated formulas and it gets messy to use the field access dot notation all over the place. So I end up putting the field values into local variables to improve readability. It works fine, but is there some clever way to avoid having to type out all the a=foo.a
lines or to have Julia parse a
as foo.a
etc?
struct Foo
a::Real
b::Real
c::Real
end
# this gets hard to read
function bar(foo::Foo)
foo.a + foo.b + foo.c + foo.a*foo.b - foo.b*foo.c
end
# this is better
function bar(foo::Foo)
a = foo.a
b = foo.b
c = foo.c
a + b + c + a*b - b*c
end
# this would be great
function bar(foo::Foo)
something clever
a + b + c + a*b - b*c
end
Upvotes: 4
Views: 1536
Reputation: 21112
As of Julia 1.6, the macros in this package look relevant: https://github.com/mauro3/UnPack.jl.
The syntax would look like:
function bar(foo::Foo)
# something clever!
@unpack a, b, c = f
a + b + c + a*b - b*c
end
In Julia 1.7, it looks like this feature will be added with the syntax
function bar(foo::Foo)
# something clever!
(; a, b, c) = f
a + b + c + a*b - b*c
end
Here is the merged pull request: https://github.com/JuliaLang/julia/pull/39285
Upvotes: 2
Reputation: 2313
Because Julia generally encourages the use of generalized interfaces to interact with fields rather than accessing the fields directly, a fairly natural way of accomplishing this would be unpacking via iteration. In Julia, objects can be "unpacked" into multiple variables by iteration:
julia> x, y = [1, 2, 3]
3-element Array{Int64,1}:
1
2
3
julia> x
1
julia> y
2
We can implement such an iteration protocol for a custom object, like Foo
. In v0.7, this would look like:
Base.iterate(foo::Foo, state = 1) = state > 3 ? nothing : (getfield(foo, state), state + 1)
Note that 3 is hardcoded (based on the number of fields in Foo
) and could be replaced with fieldcount(Foo)
. Now, you can simply "unpack" an instance of Foo
as follows:
julia> a, b, c = Foo("one", 2.0, 3)
Foo("one", 2.0, 3)
julia> a
"one"
julia> b
2.0
julia> c
3
This could be the "something clever" at the beginning of your function. Additionally, as of v0.7, you can unpack the fields in the function argument itself:
function bar((a, b, c)::Foo)
a + b + c + a*b - b*c
end
Although this does require that you mention the field names again, it comes with two potential advantages:
struct
is refactored and the fields are renamed, all code accessing the fields will remain intact (as long as the field order doesn't change or the iterate
implementation is changed to reflect the new object internals).apples
field name, you can opt to use a
.)If it's important that the field names not be repeated, you could define a macro to generate the required variables (a = foo.a; b = foo.b; c = foo.c
); however, this would likely be more confusing for the readers of your code and lack the advantages listed above.
Upvotes: 7