schemeracketlexical-scopedynamic-scopelexical-closures

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).


Solution

  • 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