Is there a way to see the lambda body in the Racket?

Let's say I have this code:

#lang racket

(define a
  ((λ (x) x)
   ((λ (y) y)
    (λ (z)
      ((λ (w) w) z)))))

      

I intuitively know this lambda expression is (uniformly) equal to (λ (z) z)

My question is if there is a way to print the body a

in case I want to see how much the function has been simplified internally by Racket.


Additional Information:

By default, if I type a

in the interpreter, I get #<procedure:y>

(it looks like it gives us a hint of how the score went). I can change the style of the output to "constructor", say, and then the result (lambda (a1) ...)

, which is closer to what I want, but I still don't know what's in the body, which is the important part.

I guess one could answer this with a deeper knowledge of the Racket scoring strategy, but I'm still wondering if the mapping of procedure organs is a thing that can happen in general.

+3


source to share


1 answer


In general, there is no way to see the body of a closure at runtime. The "body" is part of the source code. It will compile to bytecode (and then JIT machine code). Closure consists at runtime of the captured values ​​for free variables in the body and a pointer to the code.

However, you may be happy with the study of the output expand

or compile

.

Now expand

expands the syntax, so you won't be able to see any optimizations for now:

> (expand-syntax #'(define a
                     ((λ (x) x)
                      ((λ (y) y)
                       (λ (z)
                         ((λ (w) w) z))))))
(define-values (a) 
   (#%app (lambda (x) x) 
            (#%app (lambda (y) y) 
                   (lambda (z) 
                     (#%app (lambda (w) w) z)))))

      

Note what #%app

"application" means.

To see the result after applying the optimization, we need to call the compiler. The inline compile

creates bytecodes, so we use compile-zo

that converts the bytecode to structures that represent the bytecode (and they print well in repl).

> (compile-zo '((λ (x) x)
                ((λ (y) y)
                 (λ (z)
                   ((λ (w) w) z)))))
'#s((compilation-top zo 0)
    1
    #s((prefix zo 0) 0 () ())
    #s((application expr 0 form 0 zo 0)
       #s((closure expr 0 form 0 zo 0)
          #s((lam expr 0 form 0 zo 0)
             ()
             (preserves-marks single-result)
             1
             (val)
             #f
             #()
             ()
             #f
             6
             #s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
          closure64862)
       (#s((closure expr 0 form 0 zo 0)
           #s((lam expr 0 form 0 zo 0)
              y
              (preserves-marks single-result)
              1
              (val)
              #f
              #()
              ()
              #f
              6
              #s((localref expr 0 form 0 zo 0) #f 0 #f #f #f))
           y64863))))

      



There was only one application left, so the program was really simplified at compile time.

See https://github.com/soegaard/meta/blob/master/runtime/racket-eval.rkt for definition compile-zo

.

Finally, another option is to look at the machine code generated by the JIT. See https://github.com/samth/disassemble

UPDATE

If I read the bytecode correctly, this matches:

((λ (x) x)
 (λ (y) y))

      

+5


source







All Articles