19 Closure
19.1 Topics Covered
The focus of this week has been language-oriented programming (LOP) or the design of various kinds of languages. We did not teach ordinary Racket programming. Again, “design” means “using tools to create abstractions as systematically as possible.” It does not refer to the vague word that language designers often use for the careful development of syntax and the haphazard assignment of semantics.
language extension via define-syntax
With language extension, we can then realize a number of things:
kind of language |
| when covered |
module-level languages |
| |
| ||
fine-grained embedded languages |
| |
extensible embedded languages |
| see below |
reading (turning char sequences into syntax trees)
hooking functions-at-syntax-time into the expander
expanding a syntax tree until no nodes refer to macros
techniques for writing new macros:
techniques for raising the level of abstraction:
various flavors of syntax classes
lexical scope and preserving it by default:
lexical scope of the underlying language
lexical scope as the default for macros (textual substitution breaks all of this)
breaking lexical scope with datum->syntax
different lexical scope with parameters
datum->syntax truly breaks lexical scope but it doesn’t always produce stable solutions
define-syntax-parameter and syntax-parameterize create the illusion of breaking the default scoping rules but in a way that is mostly stable
building a module-level language in Racket
modules can export and import macros
the language is the first import and it provides the meaning for everything in foo for (module name language-name foo)
in particular, the language must define #%module-begin, which determines the meaning of the rest of the module
with #lang languages we can build languages from scratch, restrict existing languages, extend one, and do all of the above.
build typed module languages
build simple expression-level embedded languages.
19.2 Extending Embedded Languages
At this point, you might think that Racket has solved all problems of language-oriented programming but this is not the case. Much remains to be done.
Over the years, we have time and again encountered situations where embedded languages, such as the one you worked on this morning, themselves call for syntactic extensibility. We constructed those capabilities on an ad hoc basis:
On Monday morning, we briefly showed our way of extending the language of match patterns. See Extending match.
Matthew used a good bit of the embedded language of providing and requiring across module boundaries. See provide Macros and require Macros for the documentation on how to extend those languages.
As you may know, Racket comes with a web server that has an embedded language for dispatching from requests to places in the web server’s world. See Extending web-server/dispatch for ways of extending this DSL.
Then again, you have seen embedded languages or even built some that you might wish to extend syntactically and you can’t:
the regular expression language from this morning could clearly benefit from syntactic extensions
time and again, we see situations where macros for the language of types in Typed Racket could help.
- and there are embedded languages such as the ones in for loops that would benefit from internal macros. Take a look at the following code, taken from a recent posting to our mailing list
Building syntactic extensibility into an embedded domain-specific language is non-trivial. We can do it but our tools for doing so are not ready for prime time. In a year from now or so, they will be. And then you will be more than welcome to attend a RacketSchool again and learn all about.