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.
source to share
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))
source to share