Warm tip: This article is reproduced from stackoverflow.com, please click
common-lisp lambda racket lisp

Is there a function in Racket that returns a procedure's lambda-expression?

发布于 2020-04-10 10:09:49

In one variant of Common Lisp (I think it was CMUCL, but I might be wrong—I can't find it any more) there was a function that was (I think) called function-lambda-expression. If it got a procedure, it would print out the lambda expression that had generated it. Example:

(let ((my-thunk (lambda () (+ 1 2))))
    (write my-thunk)
    (write (function-lambda-expression my-thunk)))

This would print out something like:

#<PROCEDURE>
(LAMBDA () (+ 1 2))

It was terribly useful for debugging and exploring the language.

I'm looking for a function like this in Racket. I've looked through the Racket Documentation but I can't find anything like it. (I wouldn't be surprised if I overlooked it, however.) Is there an equivalent in Racket?

Questioner
Ashton Wiersdorf
Viewed
49
Ryan Culpepper 2020-02-01 19:14

No. Racket's lambda produces a closure that does not remember its S-expression (or syntax object) form. It does typically remember its name (or its abbreviated source location, if no name can be inferred), and that's often enough to help with debugging. (See object-name.)

You can build your own variant of lambda that has this feature, using Racket's applicable structures and a simple macro. Here's a basic example:

#lang racket

(struct exp-closure (f exp)
  #:property prop:procedure (struct-field-index f))

(define-syntax-rule (exp-lambda formals . body)
  (exp-closure (lambda formals . body)
               (quote (exp-lambda formals . body))))

(let ([my-thunk (exp-lambda () (+ 1 2))])
  (printf "fun is ~v\n" my-thunk)
  (printf "exp is ~v\n" (exp-closure-exp my-thunk))
  (printf "result is ~v\n" (my-thunk)))

This produces

fun is #<procedure:...tmp/lambda.rkt:11:19>
exp is '(exp-lambda () (+ 1 2))
result is 3

A better version of this macro might propagate the source location of the macro use to the lambda expression it creates, or the inferred name (see syntax-local-infer-name), or both.