SRFI 232: Flexible curried procedures
232: Flexible curried procedures
by Wolfgang Corcoran-Mathe, based on a design by Jason Hemann
and Daniel P. Friedman.
Status
This SRFI is currently in
final
status. Here is
an explanation
of each status that a SRFI can hold. To provide input on this SRFI, please send email to
srfi-232@
nospam
srfi.schemers.org
. To subscribe to the list, follow
these instructions
. You can access previous messages via the mailing list
archive
Received: 2022-01-05
Draft #1 published: 2022-01-07
Draft #2 published: 2022-01-24
Draft #3 published: 2022-03-17
Draft #4 published: 2022-03-21
Draft #5 published: 2022-03-28
Finalized: 2022-04-06
Abstract
Scheme lacks a flexible way to create and apply curried procedures.
This SRFI describes
curried
, a variant of
lambda
that creates true curried procedures which also
behave just like ordinary Scheme procedures. They can be applied to
their arguments one by one, all at once, or anywhere in between,
without any novel syntax.
curried
also supports
nullary and variadic procedures, and procedures created with it have
predictable behavior when applied to surplus arguments.
Rationale
Currying is a well-known tactic in functional programming, and
many languages (e.g. Standard ML and Haskell) provide convenient ways to
define curried procedures. True
currying, though, is somewhat uncommon in Scheme. Beyond the fundamental
building block of first-class procedures, Scheme provides little support
for defining curried procedures, and the clumsiness of applying such
procedures may deter programmers from using them at all. Some attempts
have been made at improving the situation. The
“higher-order lambda” notation of
SRFI 219
extends the syntax of
define
with a simple
shorthand for writing curried procedures. The complexity of applying
curried procedures remains, however:
(define ((add-c x) y)
(+ x y))

(add-c 2 3) ⇒ ; error: too many arguments

((add-c 2) 3) ⇒ 5
Perhaps because of this clumsiness, Scheme has another form which
provides something a little like currying. The
cut
operator, introduced by
SRFI 26
, is not a
currying operator; instead, it allows procedures to be easily
specialized on given parameters. However, this is not too difficult
in ordinary Scheme (
cf.
Al* Petrofsky's
comment
to the SRFI 26 mailing list), and, as such,
cut
is not
frequently seen in Scheme programs.
cut
also introduces a
novel notation using the tokens
<>
and
<...>
, which may pose an obstacle for new users.
The
curried
form described in this SRFI allows the
creation of true curried procedures which can also be treated as
normal, uncurried Scheme procedures. For example, a
curried
procedure of two arguments may be applied to one
argument to return a new procedure of one argument, or directly to two
arguments to return its value.
curried
also provides
consistent semantics for nullary and variadic procedures, and
predictable behavior for cases in which a procedure is
applied to more arguments than it accepts. All this is accomplished
without the introduction of any novel forms; syntactically,
curried
is very similar to
lambda
, which should
make it easy to adopt in existing Scheme programs.
This SRFI is not meant to replace SRFIs 26 or 219. In some
cases, these existing techniques may be preferable; the
SRFI 219 notation does much less "under the table" than does
curried
, and
cut
provides greater flexibility
in argument specialization. The
curried
form provides
another option, one that will hopefully also prove useful and
convenient.
Specification
(curried
⟨formals⟩ ⟨body⟩
Syntax: ⟨formals⟩ and ⟨body⟩ are as described in section 4.1.4 of
the R⁷RS.
Semantics: If ⟨formals⟩ is an empty list, then a
curried
expression evaluates to ⟨body⟩. Otherwise, it
evaluates to a procedure with the same environment semantics as
lambda
(R⁷RS 4.1.4). The application of a procedure
created by
curried
has the following semantics:
If ⟨formals⟩ is of the form
v₂
ₙ), then
If the procedure is applied to
arguments, where
, then “partial application”
is performed: the result is a procedure with
curried
semantics with an extended environment in which the formal
parameters
₁,
₂, …,
ₖ are
bound to the supplied arguments in order; this procedure can
accept further arguments as described in this section.
Note that, if
= 0, the result is a procedure
which is extensionally the same as the original procedure.
If the procedure is applied to
arguments, where
, then ⟨body⟩ is evaluated with the
formal parameters
₁,
₂, …,
bound to the first
arguments, and the result is applied
to the remaining
arguments. It is an
error if the value of ⟨body⟩ is not a procedure.
If the procedure is applied to exactly
arguments,
then evaluation proceeds as with
lambda
If ⟨formals⟩ is of the form
₁ …
ₙ .
ₙ₊₁), then
application proceeds as in the proper list case, except that,
if
or more arguments are supplied, then the formal
parameters
₁, …,
ₙ are bound
to the first
arguments and
ₙ₊₁ is bound to
a list of any remaining arguments, in order.
If ⟨formals⟩ is of the form ⟨variable⟩ (a single identifier),
then application proceeds as if the procedure were created with
lambda
(define-curried (
⟨variable⟩ ⟨formals⟩
⟨body⟩
Equivalent to
(define
⟨variable⟩
(curried
⟨formals⟩ ⟨body⟩
))
Examples
Simple currying
Here,
curried
is used to create procedures which can
be concisely parameterized:
(define-curried (add* x y) (+ x y))

(map (add* 2) '(1 2 3))
⇒ (3 4 5)

(define-curried (fold* proc base lis)
(fold proc base lis))

(let ((sum (fold* + 0))
(product (fold* * 1))
(lis '(1 2 3 4 5)))
(list (sum lis) (product lis)))
⇒ (15 120)
Curried polyvariadic procedures
“Polyvariadic” refers to a procedure denoted by an
expression of the form
(lambda (
₁ …
ₙ .
ₙ₊₁) ⟨body⟩)
A polyvariadic procedure created with
curried
can be
partially applied until a procedure of at least one argument
remains:
(define foo (curried (a b . rest) (list a b rest)))

((foo 1) 2 3 4) ⇒ (1 2 (3 4))

((foo 'a) 'b) ⇒ (a b ())
Extra arguments
When a
curried
procedure is applied to more arguments
than it accepts, the remaining arguments are passed to the procedure
body:
((curried (a)
(curried (b)
(curried (c) (+ a b c)))) 1 2 3) ⇒ 6
Currying with nullary procedures
Since nullary
curried
expressions evaluate to their
bodies, application “passes through” any number of
such expressions:
((curried () (curried (x y) (+ x y))) 2 3)
⇒ 5

(((curried (x)
(curried ()
(curried (y z)
(list x (* y z))))) 4 5) 6)
⇒ (4 30)
Implementation
The sample implementation is portable to R⁶RS and R⁷RS
syntax-rules
implementations and can be found
in the
repository
for this SRFI.
Acknowledgements
curried
is based heavily on the
lambda*
form created by Jason Hemann and Daniel P. Friedman and described
elegantly in “λ*: Beyond Currying”. Their work is
gratefully acknowledged.
Thanks to the SRFI editor and to those who provided feedback on
this SRFI via the mailing list or the
#scheme
IRC
channel.
References
Jason Hemann & Daniel P. Friedman, “λ*: Beyond Currying” (2013).
Available
on the Web
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice (including the
next paragraph) shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Editor:
Arthur A. Gleckler