2
votes

I wrote a fairly complex macro that manipulates an array, but it didn't work. I get the same error message even after replacing my macro with something ridiculously simple:

macro len(arr::Array)
    length(arr)
end

@len [1 2 3]

The error message I get is:

ERROR: LoadError: MethodError: no method matching @len(::Expr)

Why is Julia insisting on interpreting my input as an expression? I'm completely new to Julia, and there is obviously something I don't understand about either macros or the type system. Can someone please explain?

EDIT: Time to explain what my original macro was for. I want to take a table as input and use it to define variables in the local scope. The entries in the first column define individual variable names and the other columns define variable contents. Since a function can't do this, I need a macro. I have a working macro that takes a long (triple-quoted) string as input, but now I want it to take an array as input instead.

After this explanation, maybe I should add another subquestion: why does my other macro accept the appropriate input (it is defined as macro foo(text::String)) whereas the array version doesn't?

2
Macros can only receive and return expressions. Please read the metaprogramming section of the manual.Isaiah Norton
Specifically, look at the macro section: "Macro arguments may include expressions, literal values, and symbols." That means, I think, that you can input a literal array, e.g. [1,2,3], but not a variable containing an array. Besides, creating variables like that sounds like a generally bad idea.DNF
[1,2,3] is also interpreted as an expression, see my answer below.MyopicCat

2 Answers

2
votes

User Chris Rackauckas answered this on irc in discussion with me:

@len $a macros are on expressions, not on the values, so this sounds like something you'd want a function for (if it's really needed at all).

I'm not sure this would work there anyway. This isn't what macros are for.. it's for converting expressions into other expressions, mostly.

If you're trying to use macros on values, then you want a function (you may be able to @eval or something to make this work here, but you'll be going through great lengths to make a macro act like a function)

..as a function:

julia> function len(x)
       length(x)
       end
len (generic function with 1 method)

julia> len(a)
3

julia> a
1x3 Array{Int64,2}:
 1  2  3
2
votes

To answer my own question: this is how that "ridiculously simple" macro should have been written.

macro len(expr)
    :(length($expr))
end

This operates on an expression, not an array (thank you Chris Rackauckas and Isaiah for pointing that out). I was misled by my own experiments, since a similar macro for strings happens to work:

# MISLEADING CODE, DON'T USE THIS!
macro lenstr(str::String)
    length(str)
end

Apparently Julia allows string macros, but this is an exception to the general rule that macros operate on expressions. To illustrate how arguments appear to the macro:

macro dump(arg)
   dump(arg)
end

julia> @dump "foo bar"
String "foo bar"

julia> @dump [1,2,3]
Expr
  head: Symbol vect
  args: Array{Any}((3,))
    1: Int64 1
    2: Int64 2
    3: Int64 3
  typ: Any

julia> @dump [1 2 3]
Expr
  head: Symbol hcat
  args: Array{Any}((3,))
    1: Int64 1
    2: Int64 2
    3: Int64 3
  typ: Any

Note how [1,2,3] and [1 2 3] are seen by the macro as expressions, not arrays. This was the source of my error. By applying lessons learned here, my more complex macro that creates variables in a local scope from an array now works. Thank you all!