2
votes

I have a Julia function that takes a few input arguments and uses them to perform multiple operations. In order to get one of those operations to work properly I need to be able to compute a symbol that matches the user input. For example the function looks something like this:

function func(arg1,arg2)

    symb_arg1 =  ## get the symbol for input into arg1
    symb_arg2 =  ## get the symbol for input into arg2

    println(symb_arg1)
    println(symb_arg2)

    ## Do some operations using arg1, arg2, symb_arg1, symb_arg1

end

I am hoping to achieve the following behavior:

a = 25
b = rand(27,55,18)
func(a,b)  ## prints :a and :b

The difficulty here is to get the function to compute a symbol containing the actual name of the variable, rather than the value of the variable. This post provides the following macro that almost does what I want:

macro mymacro(ex)
    Expr(:quote,ex) # this creates an expression that looks like :(:(x + 2))
end

This macro works well for doing the following:

a = rand(27,15)
symb_a = @mymacro(a)  ## prints :a

However, using this macro inside my function will not produce the desired effect. Specifically, if I define my function as:

function func_bad(arg1,arg2)

    symb_arg1 = @mymacro(arg1)
    symb_arg2 = @mymacto(arg2)

    println(symb_arg1)
    println(symb_arg2)

    ## Do some operations using arg1, arg2, symb_arg1, symb_arg1

end

And then run the commands:

a = 25
b = rand(27,55,18)
func_bad(a,b)  ## prints :arg1 and :arg2 (instead of the desired :a and :b)

Of course, one simple (but not so elegant) solution is to add additional input arguments for the function so that the user is responsible for creating the symbols. However this is more of a last resort. I would prefer the function be able to automatically create the symbols. Any idea as to how I can modify my function or the macro to achieve this behavior?

1

1 Answers

5
votes

The simple answer is that this is not possible; there is an abstraction barrier that prevents functions from seeing implementation details of their callers. All they get is the values, which is a crucial property for robust and clear programs.

One thing you could do is write a macro to transform the call site:

@with_syms func(a, b)

into something like

func((:a,a), (:b,b))

passing symbol-value pairs into the function.

Another slightly different design would be to provide macro wrappers for all functions that need this behavior, so that calls would look like @func(a,b). You could factor out the argument list transformation to a helper function; each macro would look like

macro func(args...)
    :(func($(pair_with_symbols(args)...)))
end