219: Define higher-order lambda

by Lassi Kortela

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-219@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.

Abstract

This SRFI codifies the following shorthand syntax, which some Scheme implementations have had for a long time.

(define ((outer-name outer-args ...) inner-args ...)
  inner-body ...)

Rationale

Procedures that make other procedures are commonly used in Scheme. A shorthand syntax makes them easier to define, obviating the need to write a lambda inside a lambda.

Recall that RnRS defines a standard shorthand whereby the code:

(define foo
  (lambda (a b c)
    body ...))

can be abbreviated as:

(define (foo a b c)
  body ...)

This SRFI defines an additional shorthand which is an extrapolation of the standard one. The code:

(define (foo a b c)
  (lambda (d e f)
    body ...))

can be abbreviated as:

(define ((foo a b c) d e f)
  body ...)

The syntax generalizes further: lambdas can be nested arbitrarily deep by adding more nested lists. The key is that each nested list always be at the head position of the containing list. Each inner list adds one outer lambda; this order is intuitive when reading the S-expression left-to-right. The identifier at the head position of the innermost list becomes the name of the definition.

Apart from helping define higher-order functions, the additional shorthand syntax partially applies to the task of making partially applied functions in Scheme.

Origin

The shorthand has been in MIT Scheme since at least 1990, and may have originated in that implementation.

Survey of prior art

The following Scheme implementations have the shorthand syntax built in:

The following implementations don't have it: Bigloo, BiwaScheme, Chez Scheme, Chibi-Scheme, Cyclone, Gambit, Gerbil, Ikarus, IronScheme, Kawa, Loko, Mosh, s7, Scheme 48, SigScheme, STklos, TinyScheme, Vicare, Ypsilon.

SRFI 201: Syntactic Extensions to the Core Scheme Bindings standardizes this shorthand among several others.

Exporting the shorthand from a library

Since the shorthand is non-standard (i.e. not defined in RnRS), it can be controversial or confusing to the uninitiated programmer.

This SRFI handles the conflict by storing the shorthand version of define in a library that does not have to be imported by default. Then programmers can choose whether to import it or not. In portable code the import serves to document the dependency on this SRFI.

Specification

The shorthand version of define behaves as follows:

(define symbol expr)
defines symbol with the value
expr

(define (symbol . args) expr ...)
defines symbol with the value
(lambda args expr ...)

(define ((symbol . args1) . args) expr ...)
defines symbol with the value
(lambda args1 (lambda args expr ...))

(define (((symbol . args2) . args1) . args) expr ...)
defines symbol with the value
(lambda args2 (lambda args1 (lambda args expr ...)))

and so on.

Declarations

If the Scheme implementation supports attaching declarations such as documentation strings or optimization settings to procedures, any such declarations appearing in expr ... can apply to the outer and/or inner lambdas at the discretion of the implementation.

Importing in R6RS and R7RS

In R6RS Scheme implementations, the shorthand version of define is exported from the library (srfi :219). In R7RS Scheme implementations, it is exported from the library (srfi 219).

The shorthand is exported under the name define, which means that it shadows RnRS define.

Importing from other libraries

The shorthand may also be imported from other libraries, possibly under names other than define.

Importing by default

This SRFI does not say whether or not the shorthand is imported into the default interaction environment.

Examples

Simplest example:

(define ((greet-with-prefix prefix) suffix)
  (string-append prefix " " suffix))

(define greet (greet-with-prefix "Hello"))

(greet "there!") => "Hello there!"

With a dotted list to take a variable number of arguments:

(define ((append-to . a) . b)
  (apply append (append a b)))

((append-to)) => ()
((append-to '(1 2) '(3 4)) '(5 6) '(7 8)) => (1 2 3 4 5 6 7 8)

Avoiding the name clash

To avoid the name conflict between RnRS define and this SRFI's define, a program using this SRFI should import the RnRS base library as:

Alternatively, Scheme's import renaming can be used to import the shorthand define under a different name, in which case the same program can alternate between using the shorthand define and RnRS define. For example:

Implementation

R6RS and R7RS libraries using syntax-rules are attached.

Acknowledgements

Thanks to Arthur Gleckler and Taylor Campbell from MIT Scheme for explaining the syntax and its history.

Thanks to by Panicz Maciej Godek for writing SRFI 201. At the time of writing this SRFI, I was unaware that it contained the same shorthand.

Thanks to Marc Nieper-Wißkirchen and Panicz for hints on syntax-rules simplification.

Thanks to Göran Weinholt for collaborating on Docker containers that made it easy to do the survey.

© 2021 Lassi Kortela.

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