On Mon, May 19, 2025 at 09:10:45AM -0400, Nikolaos Chatzikonstantinou wrote: > > > > > > # 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 > > Here you can see a difference between our curries: My curry takes an > arbitrary number of arguments and curries the rest, e.g. partial > function application. >
The m4 manual's curry is indeed weak; but can easily be fixed by changing that $1 to $@ (and improving the doc comments): $ m4 define(`curry', `$1(shift($@,)_$0')dnl define(`_curry', `$@)')dnl define(`hairy', `.$2-$1.$4-$3')dnl curry(`hairy', a)(b, c, d) .b-a.d-c curry(`hairy', a, b)(c, d) .b-a.d-c curry(`hairy')(a, b, c, d) .b-a.d-c curry(`hairy', a, b, c, d)() .b-a.d-c Thanks for making me notice this! > > 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. > > Maybe I misunderstood what it means to write a truly generic lambda, > but what about this usage of my functions above? > > define(f, `lambda(`eval($1 + $2)')') > f`'(3,4) is apply(`f', 3, 4) I may not be expressing myself well, but my understanding of languages with anonymous functions is that any higher-order function that can take a function name as an argument: map(FUNC, 1, 2) => produces the list consisting of FUNC(1) and FUNC(2) should also be able to take an application of the lambda function (aka produce an anonymous function) in place of a previously named function. That means inline code: map(lambda(`eval($1+1)'), 1, 2) => produce the list 2, 3 but lambda should be usable anywhere a function name can be, and in m4 that is not trivial: map(lambda(`eval($1+1)'), 1, 2) is different than: define(`listplusone', `map(lambda(`eval(XXX+1)'), $@)') listplusone(1, 2) => I want this to produce the list 2, 3 because in the latter, if I write $1 for XXX, that gets evaluated prematurely during the expansion of listplusone, when I wanted it to be expanded late in the context of the anonymous function that is taking arguments from map. Since m4 does NOT recognize $$1 as expanding to '$1' in the helper function (the way M5 would), I can't replace the XXX with $$1 to get that effect either; I would have to resort to tricks like dol(1) and additional arguments that rewrite the string passed to lambda into something that actually produces a correct definition of listplusone. So you argue that I should instead write: define(`listplusone', `map('lambda(`eval(XXX+1)')`, $@)') but that only delays the problem, because I can dream up writing a macro that in turn wants to define a macro that uses lambda. The ultimate example would be writing the Y combinator (a lambda function that implements the lambda function), or the related Z combinator. I have tried in the past to accomplish that in m4 syntax, and failed in my attempts so far (although I haven't tried recently). So I would welcome anyone that can demonstrate a working Y combinator in m4 as a way to implement anonymous recursion. -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org