Yes, you might actually want to extend the define
form to provide capabilities that the standard define
doesn't. An example is providing decorators (thanks to uselpa's answer for inspiration):
(require (only-in racket/base (define basic-define)))
(define-syntax wrap-decorators
(syntax-rules ()
((_ () value)
value)
((_ (decorator next ...) value)
(decorator (wrap-decorators (next ...) value)))))
(define-syntax define
(syntax-rules (@)
((_ (@ decorator ...) (id . params) body ...)
(define (@ decorator ...) id (lambda params body ...)))
((_ (@ decorator ...) id value)
(define id (wrap-decorators (decorator ...) value)))
((_ other ...)
(basic-define other ...))))
(define (trace label)
(lambda (f)
(lambda args
(dynamic-wind (thunk (eprintf "enter ~a: ~s~%" label args))
(thunk (apply f args))
(thunk (eprintf "exit ~a: ~s~%" label args))))))
Now you can use it this way:
(define (@ (trace 'hypot)) (hypot x y)
(sqrt (+ (sqr x) (sqr y))))
This causes the hypot
function to be wrapped with trace
so when you call it, tracing happens:
> (hypot 3 4)
enter hypot: (3 4)
exit hypot: (3 4)
5
Or, using uselpa's memoize
function, you can use:
(define (@ memoize) (fib n)
(if (< n 2)
n
(+ (fib (sub1 n)) (fib (- n 2)))))
and get a speedy memoised fib
function. You can even trace and memoise it, showing only the actual (cache miss) invocations:
(define (@ (trace 'fib) memoize) (fib n)
(if (< n 2)
n
(+ (fib (sub1 n)) (fib (- n 2)))))
Notice, in my macro, that I imported Racket's define
as basic-define
, so that my redefined define
could delegate to it.