[racket] Saving function bodies in Beginning Student Language

From: Gregory Marton (gremio at acm.org)
Date: Sun Jun 26 23:01:40 EDT 2011

Hi folks,

In trying to improve the Bootstrap[1] teachpacks, I've written (with 
help[2]) a macro to save function bodies in a hashtable in the sky before 
actually defining them.  That way, I can programmatically break down a 
function definition into its constituent pieces and show how they're 
working, in a somewhat stepper-like way, but importantly different.
[1] http://www.bootstrapworld.org/
[2] http://www.stackoverflow.com/....

This macro works happily in every context except for the relevant one: the 
beginning student language. BSL provides nice error messages, and bunches 
of other things that we like, but this is the behavior we see:

Everywhere else:
> (define/save-source (foo x) (+ 3 x))
> (procedure-source foo)
'(» (x) (+ 3 x))            [lambda is written ? here due to ascii email]
> (procedure-name foo)
> (foo 10)


> (define/save-source (foo x) (+ x 3))
> (procedure-source foo)
(cons '? (cons (cons 'x empty) (cons (cons '+ (cons 'x (cons 3 empty)))
> (procedure-name foo)
> (foo 6)
function call: expected a defined function name or a primitive operation 
name after an open parenthesis, but found something else

So the define/save-source macro is
    (define *procedure-name-hash* (make-hash))
    (define *procedure-source-hash* (make-hash))
    (define (save-source! fn name body)
      (hash-set! *procedure-name-hash* fn name)
      (hash-set! *procedure-source-hash* fn body))
    (define (procedure-name fn)
      (hash-ref *procedure-name-hash* fn false))
    (define (procedure-source fn)
      (hash-ref *procedure-source-hash* fn false))

    ;; define/save-source
    ;; the usual define syntax, but saves off the source for later access
    ;; with procedure-source
    (define-syntax define/save-source
      (syntax-rules ()
        ((_ (name formals ...) body-expressions ...)
           (define name (? (formals ...) body-expressions ...))
           (save-source! name 'name '(» (formals ...) body-expressions ...))))
        ((_ name value)
         (define name value))))

And I guess the relevant conflicting piece of BSL is here:
In particular, this segment, which seems to check things about a function, 
and set it up for the stepper:
       ;; Well-formed lambda def:
       [(beginner-lambda arg-seq lexpr ...)
          (check-function-defn-ok stx)
          (let-values ([(defn bind-names)
                         (length (syntax->list #'arg-seq))
                         (lambda (name)
                           (with-syntax ([name name])
                              (define name
                                    #`(lambda arg-seq
                                           'stepper-skip-completely #t)
                                        lexpr ...)
                                    (syntax-local-introduce (car (syntax-e lam))))
             (and assign? bind-names))))]

So my question boils down to:
1. Is there a way that I can use the same info the stepper does, which is 
saved here, so I don't have to define my own procedure-source in the first 
place? or
2. Can I work with this to save off my source without bothering it or it 
bothering me? or
3. Can the BSL get modified to support what I'm trying to do please?

Many thanks in advance, and also many thanks to those who created the 
beginning student language for a terrific starting environment!


------ __@   Gregory A. Marton                 http://csail.mit.edu/~gremio
--- _`\<,_                                                     617-858-0775
-- (*)/ (*)                     Visualize Whirled Peas.

Posted on the users mailing list.