λ
9.0
top
← prev up next →

define2πŸ”— i

Laurent Orseau

1Define and lambdaπŸ”— i

(require define2 ) package: define2

There may be incompatibility with code that uses the keywords #:! and #:?.

The define2 collection redefines lambda and define in a (almost entirely) backward compatible way to provide the following functionalities:
  • a shortcut definition for keyword arguments to avoid the ubiquitous #:some-argsome-arg repetition,

  • a pass-through mechanism for optional keyword arguments to propagate default values without having to know them.

Example: Mandatory #:! argument
> (define (make-fruitsfruit#:!number)
(make-listnumberfruit))
> (make-fruits'apple#:number4);Notice the keyword name

'(appleappleappleapple)

Example: Optional #:? argument
> (define (make-fruits2fruit#:?[number3])
(make-listnumberfruit))
> (make-fruits2'pear)

'(pearpearpear)

> (make-fruits2'pear#:number4)

'(pearpearpearpear)

Example: Pass-through #:? argument
;Let's write a function that uses `make-fruits2` without changing
;the default value for `number`—whatever value this is.
> (define (make-two-fruitsfruit1fruit2#:?number)
(list(make-fruits2fruit1#:numbernumber)
(make-fruits2fruit2#:numbernumber)))
> (make-two-fruits'apple'banana)

'((appleappleapple)(bananabananabanana))

> (make-two-fruits'apple'banana#:number2)

'((appleapple)(bananabanana))

The usual racket syntax #:keyword[idval] can also be used with pass-through arguments:
> (define (make-fruits3fruit#:number[a-number3])
(make-lista-numberfruit))
> (define (make-fruits4fruit#:?number)
(make-fruits3fruit#:numbernumber))
> (make-fruits4'clementine)

'(clementineclementineclementine)

value

no-value :symbol?

procedure

( no-value? x)boolean?

x:any/c
no-value is an uninterned symbol representing the default value of pass-through arguments. no-value does not normally need to be used, but is provided for clarity and possibly for user enhancements.

syntax

( lambda argsbody...+)

syntax

( λ argsbody...+)

args = (pos-id...[opt-idopt-expr]...kw-arg...)
| (pos-id...[opt-idopt-expr]...kw-arg.... rest-id)
| rest-id
kw-arg = #:!id
| #:?id
| #:?[idexpr]
| keywordid
| keyword[idexpr]
Like lambda and λ from racket/base, but with support for #:! mandatory keyword arguments and #:? optional keyword arguments.

An argument of the form #:!name is equivalent to #:namename. An argument of the form #:?[nameval] is equivalent to #:name[nameval] but binds name to val only if name is no-value . An argument of the form #:?name is equivalent to #:name[nameno-value ].

This means in particular that (lambda (#:athe-a#:!a)...) is a syntax error (duplicate argument keyword), as well as (lambda (#:athe-a#:!the-a)...) (duplicate argument identifier).

syntax

( define idexpr)

(define (headargs)body...+)
Like define from racket/base, but uses lambda from define2 instead. Also supports the curried form.

2Wrapper functionsπŸ”— i

Writing wrapper functions is already simplified with the new define thanks to pass-through optional arguments, but there can still be some verbosity left due to having to repeat the argument names. define-wrapper helps with this by passing the arguments to the wrapped function automatically.

syntax

( define-wrapper (fun[wrapped-funarg...maybe-rest]
keyword-arg...)
maybe-call-wrapped
body...)
maybe-call-wrapped =
| #:call-wrappedcall-wrapped-id
arg...maybe-rest and keyword-arg... are arguments as for lambda , but keyword-arg... are restricted to keyword arguments.

The resulting function fun takes as input all the arguments arg...keyword-arg...maybe-rest. Only the arguments arg...maybe-rest are forwarded to the call to wrapped-fun. The function wrapped-fun must be defined elsewhere.

If call-wrapped-id is not provided then wrapped-fun is called in tail-position; otherwise it should be called as (call-wrapped-id) somewhere in body..., and this calls wrapped-fun with the arguments arg...maybe-rest.

More concretely (supposing that bar is already defined elsewhere),

(define-wrapper (foo(bara#:?[b'b])))

is equivalent to
(define (fooa#:?[b'b])
(bara#:bb))
and
(define-wrapper (foo(bara#:?[b'b])
#:cc)
(set!a(+ac)))
is equivalent to
(define (fooa#:?[b'b]#:cc)
(set!a(+ac))
(bara#:bb))
and
(define-wrapper (foo(bara#:?[b'b])
#:cc)
#:call-wrappedbar-wrapped
(set!a(+ac))
(define res(bar-wrapped))
(displaylnres)
res)
is equivalent to
(define (fooa#:?[b'b]#:cc)
(set!a(+ac))
(define res(bara#:bb))
(displaylnres)
res)

Note: Be careful to not use pass-through arguments if the corresponding argument in wrapped-fun is not an optional keyword argument.

For example, this fails:
> (define-wrapper (my-sort(sortl<?#:?key)))
> (my-sort'(142)<)

sort: contract violation

expected: (any/c . -> . any/c)

given: 'no-value

But this is fine:
> (define-wrapper (my-sort2(sortl<?#:?[keyvalues])))
> (my-sort2'(142)<)

'(124)

3Compile-time error checkingπŸ”— i

In standard Racket, when a function is defined with define , and is later called with the wrong number of arguments or with the wrong keywords, an error is signalled only at run time, when the function is called.

Instead, using the define form provided by define2 signals an error at compile-time.

Thus, such errors are caught much earlier than with standard Racket, for example with raco make. Furthermore, since DrRacket runs background expansion, such errors can be caught as early as the are written.

The following errors are raised at compile time:
> (define (foobar[baz'b]#:fizzfizz#:buzz[buzz#t])
#true)
> (foo)

eval:17:0: foo: missing mandatory positional arguments

header: (foo bar (baz) #:fizz (#:buzz))

at: (foo)

in: (foo)

> (foo'a'b'c#:fizz'f)

eval:18:0: foo: too many positional arguments

header: (foo bar (baz) #:fizz (#:buzz))

at: (foo 'a 'b 'c #:fizz 'f)

in: (foo 'a 'b 'c #:fizz 'f)

> (foo'a)

eval:19:0: foo: missing keywords

header: (foo bar (baz) #:fizz (#:buzz))

at: (foo 'a)

in: (foo 'a)

> (foo'a#:fizz'f#:beurre'b)

eval:20:0: foo: unknown keyword

header: (foo bar (baz) #:fizz (#:buzz))

at: #:beurre

in: (foo 'a #:fizz 'f #:beurre 'b)

For the last error, DrRacket even highlights the wrong keyword.

4AcknowledgementsπŸ”— i

Thanks to Ross Angle, Sorawee Porncharoenwase, Jack Firth, Jens-Axel Soegaard, Sam Tobin-Hochstadt, Greg Hendershott, Bogdan Popa, Matthew Flatt, Robby Findler, and Leif Anderson for their help.

top
← prev up next →

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /