How can I replace all function calls to f
with function calls to g
using a racket macro? I'm new to racket and I don't know how to process syntax objects, but I believe the use case I have in mind is something a racket macro could do. Consider the following example, where I want to replace plus
with mul
. The macro replace-plus-with-mul
just returns current-seconds
as a placeholder because I don't how what to do with the syntax object to replace plus
with mul
. Can a macro do this?
#lang racket
(define-syntax replace-plus-with-mul
(lambda (stx) #'(current-seconds)))
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define a 4)
(define b 2)
(define c (plus a b))
(replace-plus-with-mul d c) ;; (define d (mul a b))
(print d) ;; should print 8
I don't see an easy way to precisely get what you want to work, but with an additional restriction, it's certainly possible.
If you are OK with the restriction that the macro invocation must syntactically contain plus
, then simply recursively replace all plus
with mul
inside the macro
;; main.rkt
#lang racket
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define-for-syntax (replace stx)
(syntax-case stx ()
[(a . b)
(datum->syntax stx (cons (replace #'a)
(replace #'b)))]
[_
(and (identifier? stx)
(free-identifier=? #'plus stx))
#'mul]
;; FIXME: need more cases (like box or vector), but
;; this is sufficient for the demo
[_ stx]))
(define-syntax (replace-plus-with-mul stx)
(syntax-case stx ()
[(_ id expr)
#`(define id
#,(replace (local-expand #'expr 'expression '())))]))
(replace-plus-with-mul c (plus 3 (let ([plus 10]) plus)))
c ; prints 30
(plus 3 (let ([plus 10]) plus)) ; prints 13
If you are OK with the restriction that plus
that you would like to change must not already be used, like the below code:
(define (c) (plus 3 2))
(replace-plus-with-mul d (c))
Then there are several approaches to this. One is to override #%module-begin
to replace all plus
to (if (current-should-use-mul?) mul plus)
and expand replace-plus-with-mul
to (parameterize ([current-should-use-mul? #t]) ...)
. Here's the full code:
;; raquet.rkt
#lang racket
(provide (except-out (all-from-out racket)
#%module-begin)
(rename-out [@module-begin #%module-begin])
plus
mul
replace-plus-with-mul)
(define plus (lambda (x y) (+ x y)))
(define mul (lambda (x y) (* x y)))
(define current-should-use-mul? (make-parameter #f))
(define-for-syntax (replace stx)
(syntax-case stx ()
[(a . b)
(datum->syntax stx (cons (replace #'a)
(replace #'b)))]
[_
(and (identifier? stx)
(free-identifier=? #'plus stx))
#'(if (current-should-use-mul?) mul plus)]
;; FIXME: need more cases (like box or vector), but
;; this is sufficient for the demo
[_ stx]))
(define-syntax (@module-begin stx)
(syntax-case stx ()
[(_ form ...)
#'(#%module-begin (wrap-form form) ...)]))
(define-syntax (wrap-form stx)
(syntax-case stx ()
[(_ form) (replace (local-expand #'form 'top-level '()))]))
(define (activate f)
(parameterize ([current-should-use-mul? #t])
(f)))
(define-syntax (replace-plus-with-mul stx)
(syntax-case stx ()
[(_ id expr)
#`(define id (activate (lambda () expr)))]))
and
;; main.rkt
#lang s-exp "raquet.rkt"
(define (c) (plus 3 (let ([plus 10]) plus)))
(replace-plus-with-mul a (c))
a ; prints 30
(c) ; prints 13
In a sense, what you want to do needs a kind of lazy evaluation, and this is a huge semantic change. I'm not sure if there's a good way to do it while not "damaging" other code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With