2
votes

What is the name of this notation / what does it represent?

(define ((function-name arg1) arg2) (body ...))

I understand that in Racket/Scheme you can define a function in the following two ways:

(define (function-name arg1 arg2 ...) (body ...))

(define function-name (lambda (arg1 ...) (body ...))

However, I recently came across this notation in Software Design For Flexibility by Chris Hanson and Gerald Jay Sussmaan:

(define ((function-name arg1) arg2) (body ...))

I noticed that this notation works in Scheme (which the book is written in) and Racket as well.

Does anyone know the name of this notation with the function application in the define statement arguments? / What it might represent?

2
In Racket, it's called the "Curried Function Shorthand". See docs.racket-lang.org/guide/… - Sorawee Porncharoenwase

2 Answers

1
votes

This is an extension and not part of Standard Scheme. This extension has been available in MIT/GNU Scheme for quite some time, and some other implementations have adopted it.

The R7RS spec does not show a form that matches the OP posted example definition. The closest match is this in 5.3. Variable definitions:

  • (define (<variable> <formals>) <body>)
    <Formals> are either a sequence of zero or more variables, or a sequence one or more variables followed by a space-delimited period and another variable (as in a lambda expression).

The definition in question is:

(define ((function-name arg1) arg2) (body ...))

Here, (function-name arg1) would have to be considered a variable in order to match the description in the Standard; but it is not a variable, or even an identifier.

From 3.1. Variables, syntactic keywords, and regions:

An identifier that names a location is called a variable and is said to be bound to that location.

It is worth pointing out that Appendix B of Hanson and Sussman's book briefly discusses this construction:

In MIT/GNU Scheme we can use the sugar recursively, to write:

(define ((compose f g) x)
  (f (g x)))

Now, on p. 17 of the MIT/GNU Scheme Reference Manual (11.1) it is shown that:

(define (name1 name2 ...)
  expression
  expression ...)

is equivalent to:

(define name1
  (named-lambda (name1 name2 ...)
    expression
    expression ...))

Note that the syntax shown in the MIT/GNU manual shows this for lambda:

lambda formals expr expr ... [extended standard special form]

Here, formals is described as "the formal parameter list," but note that the term variable is not used. The term variable is used elsewhere in the manual, e.g., in the descriptions of let forms.

The named-lambda special form is described:

named-lambda formals expression expression ... [special form]
The named-lambda special form is similar to lambda, except that the first "required parameter" in formals is not a parameter but the name of the resulting procedure; thus formals must have at least one required parameter. This name has no semantic meaning, but is included in the external representation of the procedure, making it useful for debugging. In MIT/GNU Scheme, lambda is implemented as named-lambda, with a special name that means "unnamed".

So, the definition (define ((function-name arg1) arg2) (body ...)) can be rewritten as:

(define (function-name arg1)
  (named-lambda ((function-name arg1) arg2) (body ...)))

by the above transformation rule provided by the manual, since the formal parameter list has been extended in MIT/GNU Scheme in such a way that it is not restricted to containing variables only.

This last result can then be expanded again (recursively), using the same rule:

(define function-name
  (named-lambda (function-name arg1)
    (named-lambda ((function-name arg1) arg2)
      (body ...))))

Since named-lambda is like lambda, i.e., different only in that there is some extra metadata associated with it, but semantically identical, we can write the semantic equivalent:

(define function-name
  (lambda (arg1)
    (lambda (arg2)
      (body ...))))

This is just a definition for a higher order procedure. So the notation in question is just syntactic sugar to facilitate the definition of higher order procedures. Note that SRFI 201 aims to provide this capability for other Scheme implementations, and calls this the "higher-order define form," and also "the 'currying' variant of the define form."

Returning to the example procedure from Appendix B of Software Design for Flexibility, it can be seen to demonstrate how this syntactic sugar can be used to express the idea of function composition neatly.

(define ((compose f g) x)
  (f (g x)))

Here, compose is a procedure which takes two arguments, f and g, and which returns a procedure that takes one argument x:

1 (user) => (define my-composition (compose square cube))
;Value: my-composition
1 (user) => (my-composition 2)
;Value: 64
1
votes

This is definitely unusual, and I've never seen it explicitly documented anywhere, but it does work on at least some scheme implementations (Chicken, Racket, whatever other scheme you tried accept it, Guile doesn't).

The wording of R7RS gives some insight:

(define (〈variable〉 〈formals〉)〈body〉)

〈Formals〉 are either a sequence of zero or more variables, or a sequence of one or more variables followed by a space-delimited period and another variable (as in a lambda expression). This form is equivalent to (define〈variable〉(lambda (〈formals〉)〈body〉)).

So this:

(define ((example a) b) (printf "~A~%~A~%" a b))

can be considered equivalent to

(define (example a) (lambda (b) (printf "~A~%~A~%" a b)))

which is in turn equivalent to

(define example (lambda (a) (lambda (b) (printf "~A~%~A~%" a b))))

and the schemes that accept this notation probably do a transformation like that internally at some point as part of how they implement define-ing a function. That does match the behavior of example on chicken when defining it using the first form.