Eric Blake wrote, "Writing a truly generic
lambda() that can define an anonymous m4 macro is much tougher,
because of the inability to easily generate '$1' and friends into the
macro body without having it be prematurely expanded.  In the
literature, I have seen reference to the "M5" language, where writing
lambda functions was easier because that language specifically treats
"$$1" in a macro body as expanding to literal "$1" (so writing macros
to write other macros gets a whole lot easier),"

Well, not a whole lot easier. My demo generates the needed
parameter symbols with the "dollar macro":
             define(`DOL',`$$1')
Then, for example, DOL(2) expands to $2.

The only difficulty is to arrange for DOL to be expanded at the
right time. If it will be  protected by quotes when it should get
expanded, you must unprotect it by temporarily suspending the
quotes:
            'DOL(1)`
The only advantage of $$1 is that you don't have to worry about
how deeply it's buried in quotes. My habit is to first write DOL
bare in the inner `define' and eventually put in any needed
unquotes by examining the whole outer definition.

On Mon, May 19, 2025 at 8:57 AM Eric Blake <ebl...@redhat.com> wrote:
>
> On Sun, May 18, 2025 at 08:05:21AM -0400, Nikolaos Chatzikonstantinou wrote:
> > > And speaking of Turing complete, Doug McIlroy has an interesting demo
> > > at how not only is m4 Turing complete with all of its builtins, but if
> > > you strip it down to JUST the define macro, it is still Turing
> > > complete:
> > >
> > > https://www.tuhs.org/mailman3/hyperkitty/list/t...@tuhs.org/thread/7ZRYUMZ2QNKE24QWUFT7MRK74COT7BIQ/
> >
> > I only briefly skimmed over it, it has some cool techniques. They
> > definitely went the academic route to prove this by creating a model
> > for arithmetic. I prefer the hand-wavy route: m4 is turing complete
> > merely by supporting syscmd(). I want to share my own m4 code that I
> > wrote when I studied m4 to fix issues with Guile's Autotools macros
> > (haven't gotten around to that yet) Once I managed to implement lambda
> > functions in a language that didn't originally support them I was
> > pretty convinced of its coolness. I also managed to cram in currying,
> > in a way completing a challenge mentioned by Kenneth J. Turner in
> > "Exploiting the m4 Macro Language":
> >
> > > Unfortunately a single Curry macro cannot be defined since the number of 
> > > parameters to be fixed must be known.
> >
> > I amused the author by e-mailing him my solution. M4 has enough
> > syntactic manipulation power that you can implement a lot of
> > functional programming in it, like foldr and so on. I actually think
> > it's a good tool to teach syntactic manipulation to talented students,
> > *especially* if we can get an IDE (debugger?) around it, that shows
> > all the steps of expansion. Of course debugmode() can do this, but
> > it's a bit more technical than I'd like it to be.
> >
> > --- m4 source, cut here ---
> >
> > define(apply,`ifelse($1,`',`',$1(shift($@)))')
>
> I don't see "apply" used in the rest of your example.  Why is it here,
> and does it even do what you thing it should?
>
> If you meant for apply(`foo', 1, 2, 3) to expand to foo(1) foo(2)
> foo(3), then it should probably be:
>
> define(`apply', `ifelse(`$2',,,
>   `$1(`$2')`'apply(`$1', shift(shift($@)))')')
>
> >
> > define(id, $@)
> > define(
> >   peel,
> >   `ifelse(eval($1 == 0),1,`id(``$2'')',eval($1 ==
> > 1),1,`id(`$2')',`peel(decr($1),$2)')')
>
> Slightly more efficient to drop the evals, and declare this as:
>
> define(`peel', `ifelse($1, 0, `id(``$2'')',
>   $1, 1, `id(`$2')', `peel(decr($1),$2)')')
>
> >
> > define(
> >   randhex,
> >   `esyscmd(`od -N$1 -An -tx1 /dev/urandom | tr -d " \n"')')
>
> This depends on GNU m4.  Why is an incrementing counter not good
> enough that you need to risk arbitrary collisions with a pseudo-random
> string instead?  And if you really need random macro characters with
> just POSIX m4 (rather than depending on the GNU extension of esyscmd),
> you can (ab)use mkstemp() for that purpose, along the lines of:
>
> # randalnum() - produce 6 bytes of psuedo-random alphanumerics
> define(`randalnum', `_randalnum(mkstemp(`m4XXXXXX'))')
> define(`_randalnum', `syscmd(`rm -f $1')substr(`$1', 2)')
>
>
> >
> > define(
> >   lambda__aux,
> >   `define($1,$2)`$1'')
> >
> > define(
> >   lambda,
> >   `peel(3,``lambda__aux('lambda_``''`randhex(5), ``$1'')'')')
>
> Couldn't this also be written with just peel(2) and one less layer of
> inner quoting:
>
> define(`lambda', `peel(2, `lambda__aux('lambda_``''`randhex(5), ``$1'')')')
>
> >
> > # curry(f,arg1,...,argn)
> > # Partially applies arg1, ..., argn to f.
> > # Note that there must be at least one argument, i.e. n >= 1.
> > define(
> >   curry,
> >   `lambda(`$1(shift($@),'$`'@`)')')
>
> Here's how the m4 manual declared a version of curry:
>
> divert(`-1')
> # curry(macro, args)
> # Expand to a macro call that takes one argument, then invoke
> # macro(args, extra).
> define(`curry', `$1(shift($@,)_$0')
> define(`_curry', ``$1')')
> divert`'dnl
>
> Writing curry() in m4 is relatively easy.  Writing a truly generic
> lambda() that can define an anonymous m4 macro is much tougher,
> because of the inability to easily generate '$1' and friends into the
> macro body without having it be prematurely expanded.  In the
> literature, I have seen reference to the "M5" language, where writing
> lambda functions was easier because that language specifically treats
> "$$1" in a macro body as expanding to literal "$1" (so writing macros
> to write other macros gets a whole lot easier), unlike m4 where it is
> a literal "$" followed by the immediate expansion of "$1".  One of my
> vague ideas on branch-2.0 was to implement the ability for GNU m4 to
> implement the M5 language (although I'd have to actually research if
> there are any publicly accessible implementations of M5 to compare
> against, more than just the vague reference in that document I read).
>
> --
> Eric Blake, Principal Software Engineer
> Red Hat, Inc.
> Virtualization:  qemu.org | libguestfs.org
>
>

Reply via email to