Metaprogramming

Hello,
(Apologies for the long post, issue is not that complex, I was just trying to be descriptive to make understanding it easier)
As I understand it, metaprogramming is the tool that will help me achieve my objective.
Specifically, what I am trying to accomplish is to write a macro that returns a string or some other sequence of characters that are not actually a string but rather are code and I can directly plop that code as the input to another function, not in the sense that I am defining a variable and passing in that variable to the function but rather that the programming is returning what I would be writing myself into the function.

I will provide an example

@macro foo()
  #some code
end

vcat(@macro foo())

so say for example I wanted vcat to concatenate an arbitrary but specific number of arguments that all have different names.

Thus, I could not just return them as an array because vcat will not operate on an array of inputs. I could provide the input as a tuple of arrays but that fails for my purposes because what I am trying to vertically concatenate are functions and not arrays and thus I need to pass them in within the context of an anonymous function. Thus, I have to pass in the function names with (x) appended onto the end of them.
For example, it would need to look like that:

fooy = x -> vcat(foo1(x), foo2(x))

Now it would be easy to use a for loop and construct a string that exactly replicates the input that I would want in vcat(here) but obviously such a function would return a string and not code that I can just pass into the function in the same way as if I were to manually type that code into the parameter of vcat. This is why I suspect I need to use metaprogramming. As understand it, it would allow me to do things like this where I can have results that are julia code and not types like Strings, etc.

Is there anyone that knows how to do this?

so far I have used the following code (note this is a bit simplified so that it is easier to follow):

#the function I am trying to vertically concatenate
circ(x) = x./sqrt(sum(x .* x))
for i in 1:n_circs1
      @eval $(Symbol("encoderbottle_$i")) = circ
end

and this code creates a variables encoderbottle_1 to encoderbottle_n_circs1 (where n_circs1 is an integer and not actually n_circs1).

What this code means is that I dont have to worry about createing the variables, I just need to write a macro that creates the following Julia code:

encoderbottle_1(x), encoderbottle_2(x), ... encoderbottle_n_circs1(x)

(obviously without the … and the appropriate remainder of the pattern in place)
and then I can just plop that julia code (not plop is my word and not a technical term i dont think) into the args for the vcat function.

Any help with how to do this would be much appreciated since I still do not fully understand macros in Julia.

Thank you.

I’m not totally clear on what your end goal is here, but a couple of points which may be helpful…I suspect you don’t actually need a macro to accomplish what you want. First, you can pass an array to vcat, you just need to use the ... operator to “splat” them into individual arguments:

foo = 1
bar = [2, 3, 4]
baz = [5, 6]
vcat([foo, bar, baz]...)

This also works with functions:

funclist = [[sin, cos], [+, -], [exp, sqrt]]
vcat(funclist...)

Second point: as you’re defining them, your encoderbottle_n variables all refer to the same function (circ), so they all do the exact same thing…is your actual, non-simplified loop defining each one to do something different?

1 Like

Correct, the non-simplified loop defines each one too be something different.

The problem with this is that the result is an array and not a new function

For example, take a look at the following code:

funclist = [[+, +], [+, -], [-, -]]
3-element Array{Array{Function,1},1}:
 [+, +]
 [+, -]
 [-, -]

julia> m = vcat(funclist...)
6-element Array{Function,1}:
 +
 +
 +
 -
 -
 -

julia> m(rand(6))
ERROR: MethodError: objects of type Array{Function,1} are not callable
Use square brackets [] for indexing an Array.
Stacktrace:
 [1] top-level scope at none:0

I need to be able to pass in an array that executes on the respective function at varying inputs as is the case when you use the m(x) = vcat(circ(x), circ(x)) format

Thus, if you define it in the way m(x) = vcat(circ(x), circ(x))

you can call m(rand(2)) and it will return an array with 4 ints that are the result of applying the input to the vertically concatenated functions.

Thus, if you define it in the way m(x) = vcat(circ(x), circ(x))

you can call m(rand(2)) and it will return an array with 4 ints that are the result of applying the input to the vertically concatenated functions.

So backing up a second, IIUC you have N functions and N inputs, and you want to apply the nth function to the nth input? If so, you could use a comprehension:

funclist = [[+, +], [+, -], [-, -]]
m = vcat(funclist...)
args = rand(6)
[f(a) for (f, a) in zip(m, args)]

Or, equivalently,

map.(m, args)

If you want a single function that calls everything, you can just define it yourself:

calleverything(args) = map.(m, args)
calleverything(randn(6))
3 Likes