Macros (the general Lisp concept) are really interesting. I'm still trying to grok them myself, so if I made any mistakes please downvote and correct away.
A normal Lisp function call might look like this:
(myfunc arg1arg2arg3)
where arg1, etc can be literals, variables, expressions. Like in most programming languages, each of the arguments is evaluated and then the function is called with the evaluated values of those arguments. When it's done, a value is returned.
A macro is a lot like a function call:
(mymacro arg1arg2arg3)
However, it works differently. First of all, the arguments are passed straight into the macro - if arg1 is something like (+ 1 2), the macro will see (+ 1 2), not 3 (as the function would).
Secondly, the macro is intended to return a list, which can be a function call or another macro call. (The macro could also return a final value.) The list can then be evaluated to either expand another macro, or to call a function to produce a final value. So you can think of a macro as a special sort of function that, rather than taking in values to produce another value, takes in literal code to produce transformed code (which then can be evaluated to produce a final value).
Now, one of the uses of macros that people cite is, for example, implementing your own control flow structures. Why are the two related? Lisps have something which are known as special forms, which look like function calls but don't have their arguments evaluated before being passed in (like how macros work). The reason that matters can be described by a simple example: let's say you wanted to implement a very simple version of 'and' as a function (Clojure syntax):
However, the problem is that left-expr and right-expr will both be evaluated before my-and can even begin executing, along with the attendant side effects, etc. The problem is that functions can't choose whether or not their arguments should or shouldn't be evaluated - they just get the values of their arguments after they've been evaluated[1]. It's not really possible to implement the traditional short-circuiting 'and' using functions, or many control flow structures where you do not want to evaluate every possible branch unconditionally.
However, macros receive the literal code comprising their arguments, and they can choose to evaluate one, none, some, or all of their arguments depending on their internal logic. This makes them better suited for implementing behavior that resembles those of the Lisp special forms.
[1] This is the difference between call-by-name and call-by-value semantics. Most languages are call-by-value, as described above, but (e.g.) Scala has '=>' and Swift has '@autoclosure' to allow for some sort of call-by-name support.
Rebol has an interesting take on this, in that what Lisp calls "macros" and "functions" are unified in Rebol (as "functions") -- you can specify for each argument whether it is taken as evaluated or unevaluated.
Argument's invalid as Rebol and Red are both members of the lisp family. It is like comparing the similarities of a circle with a ellipse. https://news.ycombinator.com/item?id=8613377
I'm not sure how the argument can be invalid, just because Red and Rebol is a lisp? Sounds like both them and Dylan might be a good inspiration for a "python lisp" (not that that is what hy is doing atm, as far as I can tell). Contrasting a circle with an ellipse can be useful, sometimes ;-)
> Argument's invalid as Rebol and Red are both members of the lisp family.
I was pointing out an approach taken by Rebol that's different from most languages that support macros that might be interesting to people, not making an argument.
For non-lispers (like myself) look at Rust and Sweet.js, both from Mozilla. I've been experimenting with Sweet.js and it's very cool and powerful, and I still have a lot to learn about macros and how/where they can be used.
A normal Lisp function call might look like this:
(myfunc arg1 arg2 arg3)
where arg1, etc can be literals, variables, expressions. Like in most programming languages, each of the arguments is evaluated and then the function is called with the evaluated values of those arguments. When it's done, a value is returned.
A macro is a lot like a function call:
(mymacro arg1 arg2 arg3)
However, it works differently. First of all, the arguments are passed straight into the macro - if arg1 is something like (+ 1 2), the macro will see (+ 1 2), not 3 (as the function would).
Secondly, the macro is intended to return a list, which can be a function call or another macro call. (The macro could also return a final value.) The list can then be evaluated to either expand another macro, or to call a function to produce a final value. So you can think of a macro as a special sort of function that, rather than taking in values to produce another value, takes in literal code to produce transformed code (which then can be evaluated to produce a final value).
Now, one of the uses of macros that people cite is, for example, implementing your own control flow structures. Why are the two related? Lisps have something which are known as special forms, which look like function calls but don't have their arguments evaluated before being passed in (like how macros work). The reason that matters can be described by a simple example: let's say you wanted to implement a very simple version of 'and' as a function (Clojure syntax):
(defn my-and [left-expr right-expr] (if (not left-expr) false (if (not right-expr) false) true))
However, the problem is that left-expr and right-expr will both be evaluated before my-and can even begin executing, along with the attendant side effects, etc. The problem is that functions can't choose whether or not their arguments should or shouldn't be evaluated - they just get the values of their arguments after they've been evaluated[1]. It's not really possible to implement the traditional short-circuiting 'and' using functions, or many control flow structures where you do not want to evaluate every possible branch unconditionally.
However, macros receive the literal code comprising their arguments, and they can choose to evaluate one, none, some, or all of their arguments depending on their internal logic. This makes them better suited for implementing behavior that resembles those of the Lisp special forms.
[1] This is the difference between call-by-name and call-by-value semantics. Most languages are call-by-value, as described above, but (e.g.) Scala has '=>' and Swift has '@autoclosure' to allow for some sort of call-by-name support.