First Class Source Code Introspection


The 3L Project Scheme provides "first-class" source code introspection and manipulation. This means that the source code of definitions, whether via a define form or let or anything else, is stored and can be queried via an API.

The only element in the API is the source special form. Example usage:

(source cadr)

;; returns the list:
=> (define (cadr x) (car (cdr x)))

The source special form returns the lastest definition of a variable or "variable like" entity. This means that if set! is used to redefine a variable the source special form will return the definition resulting from the call to set!.

An example:

(define x 10)

(source x)
=> (define x 10)

(number? (third (source x)))
=> #t

(set! x "Hi!")

(source x)
=> (set! x "Hi!")

(string? (third (source x)))
=> #t

Also note that the source system tracks macro definitions as well.

The first-class source code part of the introspection API has some major development related implications.

First, it can be used to quickly fix a bug or add a feature to some part of a running program when used with eval. It can be hooked in to the running REPL to "paste" the code into the REPL line for easy manipulation and redefinition. It can also be hooked in to your favorite editor to paste the definition in to the a source file. Combined with other introspection features it could even provide synchronization between the running program and its source code.

Second, it can be used to build documentation resources. For example, documentation can be automatically generated for function signatures. When working with an introspection API that lists variables and definitions currently in scope documentation could be generated for the entire running program or a program for the sole purpose of generating the documentation for future reference. This also makes it easy to provide a dynamic documentation explorer. (The 3L Project Scheme also provides a first-class documentation API which combined with the introspection API can generate comprehensive documentation easily.)

Third, it can be used to dynamically instrument or uninstrument code for debugging purposes; explicit support within the language runtime is not required for creating a debugger. This also means that it is easy to add new debuggers and test out different debugging methods by simply creating and loading a library.

Fourth, it can similarly be used to instrument or uninstrument code for profiling and again that removes the need to explicitly support profiling in the language runtime itself.

There are almost certainly other development features that can exploit the source API that will be explored in the future. The primary goal of the source API is to provide a simple building block for other development features.