[racket] obtaining name of module-level binding
> Is 'Semantics Engineering with PLT Redex' where I should get started on
> language-engineering?
Concretely, I think Robby's suggestion will look something like this:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket
;; Let's create a custom structure for procedures that remember certain things.
(struct proc/data (proc data)
#:property prop:procedure (lambda (pd . args)
(apply (proc/data-proc pd) args)))
;; We can manually create such instances...
(define f (proc/data
;; implementation:
(lambda (x)
(* x x))
;; with associated metadata:
"this is the square function"))
;; It can be called:
(f 16)
;; but we can also retrieve its metadata:
(proc/data-data f)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
For more information on this, see:
http://docs.racket-lang.org/reference/procedures.html#(def._((lib._racket/private/base..rkt)._prop~3aprocedure))
Now that we can record metadata with a procedure-like value, let's
look at what kinds of data we'd like to capture. Concretely, your
question sounds like you want something about module-level bindings.
That kind of information should be accessible at compile-time. We can
try to write a macro to save that information for us.
Continuing: let's have a "def" form that remembers some information we
determine ate compile time. Save the above coide into a file called
"def.rkt". We'll be adding the following extra lines to it:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; I think we need the following definition to use splicing-let-syntax:
(require racket/splicing)
;; This "def" will try to automatically bundle a metadata or a
proc/data; this information is
;; the module path, which it can determine during compilation.
(define-syntax (def stx)
(syntax-case stx ()
[(_ (name . args) body ...)
(syntax/loc stx
;; Question to others: how can this be simplified?
(splicing-let-syntax ([get-literal-metadata
(lambda (stx)
#`(#%datum . #,(format "~s"
(variable-reference->resolved-module-path
(#%variable-reference)))))])
(define name (proc/data
(lambda args body ...)
(get-literal-metadata)))))]))
(provide def
(struct-out proc/data))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I'm not sure if this is the cleanest way to do it, so hopefully
someone can help correct me. :)
This creates a "def" form that can define functions. It uses the
proc/data we defined earlier, and it does a minor bit of compile-time
computation to figure out module paths.
Finally, let's try using it! Create 'def-test.rkt' with the following content:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#lang racket
(require "def.rkt")
(def (h x)
(string-append "hi, I'm " x))
(h "bob")
(proc/data-data h)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
If you run this program, you should see:
##############################
"hi, I'm bob"
"#<resolved-module-path:'def-test>"
##############################
which shows that the "h" function we create with 'def' is a callable
thing, and it also has runtime metadata that remembers where it came
from.
For more information, see: http://docs.racket-lang.org/guide/pattern-macros.html