Proposal: Add to Julia a macro to make operations between elements of structures easier

Hi guys,

I would like to propose a macro to be added to Julia to make the operations between elements of structures easier.

For example, let’s say you have a structure with lots of fields called Configurations. Like:

struct Configuration{T}
    a::T
    b::T
    c::T
    d::T
    ...
end

Then, you need to compute some variables that depends on the values inside this structure. I can do something like:

conf.a * exp( - (conf.b)^2 / (conf.c) )

However, wouldn’t it be nice if we can have a macro in Julia to do something like:

@instruct conf a*exp( -b^2/c )

and it automatically replace all variables to the ones in the struct conf?

I know we have Parameters.jl, but this is something more simple.

What do you think?

The nice thing about macros is that there’s no particular reason they need to live in Julia itself–you can always just make your own package to define the macro and then anyone who wants it can use it.

That said, I’m not sure what you’re asking for is possible. Even in the example you gave, how would the macro know that a and b are fields of conf, but exp is not? The macro just sees them all as symbols. You’d have to also pass the set of symbols to be extracted, something like: @instruct(conf, (a, b, c), a * exp(-b^2/c))

3 Likes

Hum OK!

I actually have no idea how this can be done, just thought it should be nice :slight_smile:

The macro can use the fieldnames and if a symbol belong to it, then replace by getfield. Is it possible?

The macro sees only the :conf symbol. It doesn’t know it is referencing a Configuration{T} type.
fieldnames would be called on a symbol not on the instance of a Configuration type.

2 Likes

Hum, maybe a @generated function?

An @generated function would have access to the type, so you could conceivably operate on its fieldnames(), yes. But which function should be generated? You’d have to make every function that wants to use this feature an @generated one.

Or, you could just use Parameters.jl and do:

@unpack a, b, c = conf
a * exp(-b ^2 / c)

which already works :slightly_smiling_face:

1 Like

what’s old is new again ! you want the semantics of the Pascal “with” statement.

conf = Configuration(1,2,3,4)

@with conf
  x = a * b + c * d
end

or even

@with conf
  a= 1
  b= 2
  c= 3
  d= 4
end

It seems like it should be very possible to define something like that.
After all, Pascal could do it…

Here’s a nice example of how the “with” statement works

https://freepascal.org/docs-html/ref/refsu62.html

1 Like

I knew I have already used this! :smiley:

At type construction of Conf a macro @with_Conf could be defined, similarly to the @unpack_Conf macro which Parameters.jl defines. And then:

@with_Conf
  x = a * b + c * d
end`

However, as with @unpack_Conf there are pitfalls, say:

@with_Conf
  x = sin(pi*a)
end`

And then years later you change the Conf type to contain a field pi and now above code does something else…

5 Likes

Someone may have mentioned this, but it may be worth checking out Parameters.jl. It doesn’t do exactly what you want, but it does similar things that save quite a bit of coding time.

1 Like

I actually use Parameters.jl a lot! :slight_smile: This is more simple.

The suggestion of @mauro3 is very good indeed, and I think Parameters.jl is a good place to put such feature if @mauro3 allows.

PR welcome :wink:

3 Likes

I will do my best and send you a PR :slight_smile:

DataFramesMeta (and a few other packages) do something similar by using Symbols as markers, so it’d become something like:

@with(x, :a * exp(-:b ^2 / :c))
@with x begin
       z = :a * exp(-:b ^2 / :c)
end
5 Likes