简体   繁体   中英

temporarily overwrite a globally defined function in Scheme let block?

suppose I have the following functions:

(define (g x) (f x))
(define (f x) (+ 1 x))

I would like to temporarily call g with a different f . For example, something like this:

(let ((f (lambda (x) (+ 2 x))))
  (g 5))

I would like the code above to evaluate to 7, but it doesn't. Instead, it evaluates to 6, since g calls the f outside the scope of the let .

Is there a way to do this without redefining g inside the let , and without inlining the entire body of the definition of g in the let ? (In practice, g may be a very large, complicated function).

What you are asking for is dynamic rather than lexical binding of 'f'. R6RS and R7RS support this with parameters. This will do what you want:

(define f (make-parameter (lambda (x) (+ 1 x))))
(define (g x) ((f) x))

(display (g 5))(newline)

(parameterize ((f (lambda (x) (+ 2 x))))
  (display (g 5))(newline))

I'm not sure that you can, but I'm by no means a Scheme expert.

I realise that you're trying to achieve this without redefining g inside the let , but how about:

(define (h f x) (f x))
(define (g x) (h f x))
(define (f x) (+ 1 x))

(let ((f (lambda (x) (+ 2 x))))
  (h f 5))

That way, you preserve the behaviour of g where it's currently being called. But where you want to temporarily have a different behaviour, you can call h instead.

A bit more code for clarification:

(let ((f (lambda (x) (+ 2 x))))
  (display (g 5)) ; 6
  (newline)
  (h f 5))        ; 7

You could use an optional parameter in g to pass the f from the let expression.

(define (g x . args)
  (if (null? args)
    (f x)
    ((car args) x)))

and

(let ((f (lambda (x) (+ 2 x))))
  (g 5 f))

I found a way to do exactly what I wanted, although I have a feeling many people will not consider this kosher:

(define (g x) (f x))

(define (f x) (+ 1 x))

(let ((old-f f))
  (set! f (lambda (x) (+ 2 x)))
  (let ((ans (g 5)))
    (set! f old-f)
    ans))
; -> 7

(g 5) ; -> 6

edit In response to the comment below, I wasn't even aware that fluid-let was a thing. It even already works on MIT-Scheme. That's actually exactly what I needed. If commenter below posts something like this as an answer, it will be made the accepted answer:

(define (g x) (f x))

(define (f x) (+ 1 x))

(fluid-let ((f (lambda (x) (+ x 2))))
  (g 5)) ; -> 7

(g 5) ; -> 6

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM