SRFI 232: Flexible curried procedures

This egg provides curried, a lambda-like form that creates flexible curried procedures. These procedures can be applied to arguments one by one, or all at once.

  1. SRFI 232: Flexible curried procedures
  2. SRFI description
  3. Author
  4. Informal specification
    1. More examples
      1. Simple currying
      2. Curried polyvariadic procedures
      3. Extra arguments
      4. Currying with nullary procedures
  5. About this egg
    1. Author & maintainer
    2. Repository
    3. Version history
    4. License

SRFI description

This page informally documents the forms exported by the egg. For a full description of SRFI 232, see the SRFI document.

Author

by Wolfgang Corcoran-Mathe, based on the lambda* form created by Jason Hemann and Daniel P. Friedman.

Informal specification

[syntax] (curried formals body)

formals is anything that could appear as the formals argument of a lambda expression. As with lambda, body is a sequence of one or more expressions.

Normally, a curried evaluates to a procedure which can be applied to any number of arguments. If it is applied to fewer parameters than its formals list indicates, then the given arguments are bound and a new curried procedure which accepts more parameters is returned. When all parameters have been bound, the body of the procedure is evaluated. For example,

(let* ((foo0 (curried (x y z) (list x (list y z))))
       (foo1 (foo0 'a 'b)))
  (foo1 'c))
;; => (a (b c))

Here, foo0 is applied to two arguments, giving another procedure in which x is bound to a and y to b. This is then applied to c, which gives a value for z. All of the original parameters now have bindings, so the body of the procedure is evaluated.

curried procedures can also be applied "all at once":

(foo0 'a 'b 'c) ; => (a (b c))

If a normal curried procedure is applied to too many parameters (i.e. more than appear in its formals list), then the rest are passed to the value of the body (which must be a procedure):

(let ((bar (curried (a b)
             (lambda (c d) (+ (* a b) (- c d))))))
  (bar 2 3 7 5))
; => 8

A curried procedure with a "dotted-tail" formals list will bind a list of any excess arguments to the final parameter:

(let ((foo (curried (a b . rest)
             (apply + (* a b) rest))))
  (list ((foo 2) 3 4)
        (foo 2 3 4 5)
        (foo 2 3)))
;; => (10 15 6)

The procedure is applied as soon as all of the normal parameters have bindings. So, in the above example, (foo 2 3) binds rest to ().

There are a few important corner cases. A curried procedure with an empty formals list evaluates to its body:

(curried () 'b) ; => b

If formals is a single identifier, then curried works like lambda:

(let ((baz (curried xs (list (car xs) (cdr xs)))))
  (baz 2 3 4 5))
; => (2 (3 4 5))
[syntax] (define-curried (p-name . formals) expr ...)

Defines p-name to be a new curried procedure. Equivalent to

(define ⟨p-name)
  (curried formals expr ...))

More examples

Simple currying

Here, curried is used to create procedures which can be concisely parameterized:

(import (only (srfi 1) fold))
(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" = "with dotted-tail formals".)

(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

((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)

About this egg

Author & maintainer

Wolfgang Corcoran-Mathe

Contact: wcm at sigwinch dot xyzzy minus the zy

Repository

GitHub

Version history

0.1
(???) Ported to CHICKEN 5.

License

© 2022 Wolfgang Corcoran-Mathe.

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.