Classic lambda calculus defines the Curry function to only apply one
additional argument; but it is just as easy in m4 to curry an
arbitrary number of fixed arguments coupled with an arbitrary number
of extra arguments.  It is also worth documenting how to provide a
name to a curried function.

* examples/curry.m4: Accept multiple extra arguments, and improve
documentation.
* doc/m4-texi (Composition): Reflect it into the manual.
Reported-by: Nikolaos Chatzikonstantinou
https://lists.gnu.org/archive/html/m4-discuss/2025-05/msg00056.html
---
 doc/m4.texi       | 51 +++++++++++++++++++++++++++++++++++------------
 examples/curry.m4 | 26 +++++++++++++++++++-----
 2 files changed, 59 insertions(+), 18 deletions(-)

diff --git a/doc/m4.texi b/doc/m4.texi
index 612ed945..d8befa52 100644
--- a/doc/m4.texi
+++ b/doc/m4.texi
@@ -4134,21 +4134,26 @@ Composition
 @cindex currying arguments
 @cindex argument currying
 Another interesting composition tactic is argument @dfn{currying}, or
-factoring a macro that takes multiple arguments for use in a context
-that provides exactly one argument.
+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).

 @deffn Composite curry (@var{macro}, @dots{})
-Expand to a macro call that takes exactly one argument, then appends
-that argument to the original arguments and invokes @var{macro} with the
+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 @var{macro} with the
 resulting list of arguments.
 @end deffn

 A demonstration of currying makes the intent of this macro a little more
 obvious.  The macro @code{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 @code{reverse} with two arguments
-for each definition of a macro stack.  This example uses the file
-@file{m4-@value{VERSION}/@/examples/@/curry.m4} included in the
+coupled with currying, we can invoke @code{reverse} with two or more
+arguments for each definition of a macro stack (that is, we factor out
+the early argument @code{`4'} and couple it with a different late
+argument for each member of the stack @code{a}).  This example uses the
+file @file{m4-@value{VERSION}/@/examples/@/curry.m4} included in the
 distribution.

 @comment examples
@@ -4163,14 +4168,18 @@ Composition
 @result{}
 stack_foreach(`a', `:curry(`reverse', `4')')
 @result{}:1, 4:2, 4:3, 4
+stack_foreach(`a', `:curry(`reverse', `4', `5')')
+@result{}:1, 5, 4:2, 5, 4:3, 5, 4
 curry(`curry', `reverse', `1')(`2')(`3')
 @result{}3, 2, 1
+curry(`reverse', `1')(`2', `3')
+@result{}3, 2, 1
 @end example

 Now for the implementation.  Notice how @code{curry} leaves off with a
 macro name but no open parenthesis, while still in the middle of
 collecting arguments for @samp{$1}.  The macro @code{_curry} is the
-helper macro that takes one argument, then adds it to the list and
+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
 @code{shift} call allows currying to also work for a macro that takes
 one argument, although it often makes more sense to invoke that macro
@@ -4181,11 +4190,27 @@ Composition
 $ @kbd{m4 -I examples}
 undivert(`curry.m4')dnl
 @result{}divert(`-1')
-@result{}# curry(macro, args)
-@result{}# Expand to a macro call that takes one argument, then invoke
-@result{}# macro(args, extra).
-@result{}define(`curry', `$1(shift($@@,)_$0')
-@result{}define(`_curry', ``$1')')
+@result{}# curry(macro, args...)
+@result{}# Perform partial argument application on the given macro.  This
+@result{}# expands to an unspecified macro name that accepts one or more extra
+@result{}# arguments, and appends those to the args supplied to the original
+@result{}# curry call as the overall set of arguments to pass to macro.  That
+@result{}# is, curry(`macro', args1...)(args2...) is the same as invoking
+@result{}# macro(args1..., args2...).
+@result{}#
+@result{}# Most often, argument currying comes in handy when given a context
+@result{}# that normally takes a macro name to call with one argument, but
+@result{}# where you want to combine that variable argument with other fixed
+@result{}# arguments to forward to a macro that takes multiple arguments.  For
+@result{}# example, given a "foreach" macro that calls its first argument once
+@result{}# for each successive argument, "foreach(`curry(`mult', 3)', 1, 2, 3)"
+@result{}# would behave the same as "mult(3, 1), mult(3, 2), mult(3, 3)".
+@result{}#
+@result{}# It is also possible to create a named function curry.  For example:
+@result{}# define(`mult3', `curry(`mult', 3)($1)')
+@result{}# Later use of mult3(value) will compute the same as mult(3, value).
+@result{}define(`curry', `$1(shift($@@,)_curry')
+@result{}define(`_curry', `$@@)')
 @result{}divert`'dnl
 @end example

diff --git a/examples/curry.m4 b/examples/curry.m4
index 00997c38..9d8eef79 100644
--- a/examples/curry.m4
+++ b/examples/curry.m4
@@ -1,7 +1,23 @@
 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')')
+# 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
-- 
2.49.0


_______________________________________________
M4-patches mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/m4-patches

Reply via email to