Skip to content

Instantly share code, notes, and snippets.

@dharmatech
Created January 23, 2010 18:12
Show Gist options
  • Save dharmatech/284714 to your computer and use it in GitHub Desktop.
Save dharmatech/284714 to your computer and use it in GitHub Desktop.

Introduction

There are various approaches to optional named arguments in Scheme.

It's easy to define a procedure which accepts optional arguments:

(define (xyz . args) ...)

It's also possible to have the procedure itself handle named arguments:

(define (xyz . args)
  (let ((args (append args '(x 10 y 20 z 30))))
    (let ((x (cadr (member 'x args)))
          (y (cadr (member 'y args)))
          (z (cadr (member 'z args))))
      (list x y z))))

The parameters 'x', 'y', and 'z' have default values 10, 20, and 30. Example:

> (xyz)
(10 20 30)
> (xyz 'x 100)
(100 20 30)
> (xyz 'y 200)
(10 200 30)
> (xyz 'z 300 'y 200 'x 100)
(100 200 300)

One property of that approach is that the named argument handling occurs at runtime, each time 'xyz' is called.

The 'named-arguments' macro takes a different approach. It generates a macro which is responsible for handling the named arguments at expand time. There is no overhead for handling the named arguments at runtime.

Example

Define a 'point' record type:

(define-record-type point (fields x y))

This generates a 'make-point' constructor for us. Let's make a 'create-point' named arguments macro:

(named-arguments create-point ((x 0) (y 0)) make-point)

And try it out:

> (create-point)
#[point 0 0]
> (create-point (x 10))
#[point 10 0]
> (create-point (y 20))
#[point 0 20]
> (create-point (y 20) (x 10))
#[point 10 20]
> (create-point (x 10) (y 20))
#[point 10 20]

Other implementations

(xitomatl keywords) by Derick Eddington.

(library (named-arguments)
(export named-arguments)
(import (rnrs) (dharmalab misc gen-id))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax define-entry-macro-helper
(syntax-rules ()
((define-entry-macro-helper name-helper (key ...) name*)
(define-syntax name-helper
(lambda (stx)
(syntax-case stx ()
((name-helper tbl)
(with-syntax (((value (... ...))
(map (lambda (x)
(datum->syntax #'name-helper
(cdr (assq x (syntax->datum #'tbl))))
)
(syntax->datum #'(key ...)))))
#'(name* value (... ...))))
((name-helper tbl (k v) rest (... ...))
(with-syntax ((new-tbl (datum->syntax #'name-helper
(cons (cons (syntax->datum #'k)
(syntax->datum #'v))
(syntax->datum #'tbl)))))
#'(name-helper new-tbl rest (... ...))))
))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax define-entry-macro
(syntax-rules ()
((define-entry-macro name ((key val) ...) name-helper)
(define-syntax name
(syntax-rules ()
((name (k v) (... ...))
(name-helper ((key . val) ...) (k v) (... ...))))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-syntax named-arguments
(lambda (stx)
(syntax-case stx ()
((named-arguments name ((parameter value) ...) name*)
(with-syntax ((name-helper (gen-id #'named-arguments #'name "-helper")))
#'(begin
(define-entry-macro-helper name-helper (parameter ...) name*)
(define-entry-macro name ((parameter value) ...) name-helper)))))))
;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment