### 6.7 Building macros with macros ¶

Since m4 is a macro language, it is possible to write macros that can build other macros. First on the list is a way to automate the creation of blind macros.

Composite: define_blind(name, [value]) ¶

  Defines name as a blind macro, such that name will expand to value only when given explicit arguments. value should not be the result of `defn` (see Renaming macros). This macro is only recognized with parameters, and results in an empty string.

Defining a macro to define another macro can be a bit tricky. We want to use a literal ‘`$#`’ in the argument to the nested `define`. However, if ‘`$`’ and ‘`#`’ are adjacent in the definition of `define_blind`, then it would be expanded as the number of arguments to `define_blind` rather than the intended number of arguments to name. The solution is to pass the difficult characters through extra arguments to a helper macro `_define_blind`. When composing macros, it is a common idiom to need a helper macro to concatenate text that forms parameters in the composed macro, rather than interpreting the text as a parameter of the composing macro.

As for the limitation against using `defn`, there are two reasons. If a macro was previously defined with `define_blind`, then it can safely be renamed to a new blind macro using plain `define`; using `define_blind` to rename it just adds another layer of `ifelse`, occupying memory and slowing down execution. And if a macro is a builtin, then it would result in an attempt to define a macro consisting of both text and a builtin token; this is not supported, and the builtin token is flattened to an empty string.

With that explanation, here’s the definition, and some sample usage. Notice that `define_blind` is itself a blind macro.

    $ m4 -d
    define(`define_blind', `ifelse(`$#', `0', ``$0'',
    `_$0(`$1', `$2', `$'`#', `$'`0')')')
    ⇒
    define(`_define_blind', `define(`$1',
    `ifelse(`$3', `0', ``$4'', `$2')')')
    ⇒
    define_blind
    ⇒define_blind
    define_blind(`foo', `arguments were $*')
    ⇒
    foo
    ⇒foo
    foo(`bar')
    ⇒arguments were bar
    define(`blah', defn(`foo'))
    ⇒
    blah
    ⇒blah
    blah(`a', `b')
    ⇒arguments were a,b
    defn(`blah')
    ⇒ifelse(`$#', `0', ``$0'', `arguments were $*')

Another interesting composition tactic is argument currying, or wrapping a macro that normally takes multiple arguments in a way that the initial arguments can be supplied early, and the remaining arguments are applied later in a context that normally expects a macro name that accepts fewer arguments (often just one).

Composite: curry(macro, …) ¶

  Accept a list of early arguments, then expand to an unspecified macro name that takes one or more late arguments, appends those extra arguments to the earlier, and finally invokes macro with the resulting list of arguments.

A demonstration of currying makes the intent of this macro a little more obvious. The macro `stack_foreach` mentioned earlier is an example of a context that provides exactly one argument to a macro name. But coupled with currying, we can invoke `reverse` with two or more arguments for each definition of a macro stack (that is, we factor out the early argument ``4'` and couple it with a different late argument for each member of the stack `a`). This example uses the file `m4-1.4.21/examples/curry.m4` included in the distribution.

    $ m4 -I examples
    include(`curry.m4')include(`stack.m4')
    ⇒
    define(`reverse', `ifelse(`$#', `0', , `$#', `1', ``$1'',
                              `reverse(shift($@)), `$1'')')
    ⇒
    pushdef(`a', `1')pushdef(`a', `2')pushdef(`a', `3')
    ⇒
    stack_foreach(`a', `:curry(`reverse', `4')')
    ⇒:1, 4:2, 4:3, 4
    stack_foreach(`a', `:curry(`reverse', `4', `5')')
    ⇒:1, 5, 4:2, 5, 4:3, 5, 4
    curry(`curry', `reverse', `1')(`2')(`3')
    ⇒3, 2, 1
    curry(`reverse', `1')(`2', `3')
    ⇒3, 2, 1

Now for the implementation. Notice how `curry` leaves off with a macro name but no open parenthesis, while still in the middle of collecting arguments for ‘`$1`’. The macro `_curry` is the helper macro that takes one or more late arguments, then adds to the list and finally supplies the closing parenthesis. The use of a comma inside the `shift` call allows currying to also work for a macro that takes one argument, although it often makes more sense to invoke that macro directly rather than going through `curry`.

    $ m4 -I examples
    undivert(`curry.m4')dnl
    ⇒divert(`-1')
    ⇒# curry(macro, args...)
    ⇒# Perform partial argument application on the given macro.  This
    ⇒# expands to an unspecified macro name that accepts one or more extra
    ⇒# arguments, and appends those to the args supplied to the original
    ⇒# curry call as the overall set of arguments to pass to macro.  That
    ⇒# is, curry(`macro', args1...)(args2...) is the same as invoking
    ⇒# macro(args1..., args2...).
    ⇒#
    ⇒# Most often, argument currying comes in handy when given a context
    ⇒# that normally takes a macro name to call with one argument, but
    ⇒# where you want to combine that variable argument with other fixed
    ⇒# arguments to forward to a macro that takes multiple arguments.  For
    ⇒# example, given a "foreach" macro that calls its first argument once
    ⇒# for each successive argument, "foreach(`curry(`mult', 3)', 1, 2, 3)"
    ⇒# would behave the same as "mult(3, 1), mult(3, 2), mult(3, 3)".
    ⇒#
    ⇒# It is also possible to create a named function curry.  For example:
    ⇒# define(`mult3', `curry(`mult', 3)($1)')
    ⇒# Later use of mult3(value) will compute the same as mult(3, value).
    ⇒define(`curry', `$1(shift($@,)_curry')
    ⇒define(`_curry', `$@)')
    ⇒divert`'dnl

Unfortunately, with M4 1.4.x, `curry` is unable to handle builtin tokens, which are silently flattened to the empty string when passed through another text macro. This limitation will be lifted in a future release of M4.

Putting the last few concepts together, it is possible to copy or rename an entire stack of macro definitions.

Composite: copy(source, dest) ¶
Composite: rename(source, dest) ¶

  Ensure that dest is undefined, then define it to the same stack of definitions currently in source. `copy` leaves source unchanged, while `rename` undefines source. There are only a few macros, such as `copy` or `defn`, which cannot be copied via this macro.

The implementation is relatively straightforward (although since it uses `curry`, it is unable to copy builtin macros, such as the second definition of `a` as a synonym for `divnum`. See if you can design a version that works around this limitation, or see Answers).

    $ m4 -I examples
    include(`curry.m4')include(`stack.m4')
    ⇒
    define(`rename', `copy($@)undefine(`$1')')dnl
    define(`copy', `ifdef(`$2', `errprint(`$2 already defined
    ')m4exit(`1')',
       `stack_foreach(`$1', `curry(`pushdef', `$2')')')')dnl
    pushdef(`a', `1')pushdef(`a', defn(`divnum'))pushdef(`a', `2')
    ⇒
    copy(`a', `b')
    ⇒
    rename(`b', `c')
    ⇒
    a b c
    ⇒2 b 2
    popdef(`a', `c')c a
    ⇒ 0
    popdef(`a', `c')a c
    ⇒1 1