6.0.1.11
9.8 Top
You’ll probably never invoke this module directly. But it’s implicitly imported into every Pollen markup file. And if you don’t know what it does, you might end up surprised by some of the behavior you get.
In standard Racket,
#%top is the function of last resort, called when
id is not bound to any value. As such, it typically reports a syntax error.
Examples: |
; Let's call em without defining it | > (em "Bonjour") | em: undefined; | cannot reference undefined identifier | ; (em "Bonjour") is being converted to ((#%top . em) "Bonjour") | ; So calling ((#%top . em) "Bonjour") will give the same result | > ((#%top . em) "Bonjour") | em: undefined; | cannot reference undefined identifier |
|
In the Pollen markup environment, however, this behavior is annoying. Because when you’re writing X-expressions, you don’t necessarily want to define all your tags ahead of time.
So Pollen redefines #%top. For convenience, Pollen’s version of #%top assumes that an undefined tag should just refer to an X-expression beginning with that tag (and uses make-tag-function to provide this behavior):
Examples: |
; Again, let's call em without defining it, but using pollen/top | > (require pollen/top) | | > (em "Bonjour") | '(em "Bonjour") | ; (em "Bonjour") is still being converted to ((#%top . em) "Bonjour") | ; But now, ((#%top . em) "Bonjour") gives a different result | > ((#%top . em) "Bonjour") | '(em "Bonjour") |
|
The good news is that this behavior means you use any tag you want in your markup without defining it in advance. You can still attach a function to the tag later, which will automatically supersede #%top.
Examples: |
> (define (em x) `(span ((style "font-size:100px")) ,x)) | | > (em "Bonjour") | '(span ((style "font-size:100px")) "Bonjour") |
|
The bad news is that you’ll never get an “undefined identifier” error. These undefined identifiers will happily sail through and be converted to tags.
Examples: |
> (require pollen/top) | | > (define (em . xs) `(span ((style "font-size:100px")) ,@xs)) | | ; There's a typo in my tag | > (erm "Bonjour") | '(erm "Bonjour") |
|
This isn’t a bug. It’s just a natural consequence of how Pollen’s #%top works. It can, however, make debugging difficult sometimes. Let’s suppose my markup depends on very-important-function, which I don’t import correctly.
Examples: |
> (require pollen/top) | | > (module vif racket/base | (define (very-important-function . xs) `(secrets-of-universe ,@xs))) |
| | ; Forgot to (require 'vif) | > (very-important-function "Bonjour") | '(very-important-function "Bonjour") |
|
So the undefined-function bug goes unreported. Again, that’s not a bug in Pollen — there’s just no way for it to tell the difference between an identifier that’s deliberately undefined and one that’s inadvertently undefined. If you want to guarantee that you’re invoking a defined identifier, use def/c.
Invoke
id if it’s a defined identifier, otherwise raise an error. This form reverses the behavior of
#%top (in other words, it restores default Racket behavior).
Recall this example from before. In standard Racket, you get an undefined-identifier error.
Examples: |
> (module vif racket/base | (define (very-important-function . xs) `(secrets-of-universe ,@xs))) |
| | ; Forgot to (require 'vif) | > (very-important-function "Bonjour") | very-important-function: undefined; | cannot reference undefined identifier |
|
But with pollen/top, the issue is not treated as an error.
Examples: |
> (require pollen/top) | | > (module vif racket/base | (define (very-important-function . xs) `(secrets-of-universe ,@xs))) |
| | ; Forgot to (require 'vif) | > (very-important-function "Bonjour") | '(very-important-function "Bonjour") |
|
By adding def/c, we restore the usual behavior, guaranteeing that we get the defined version of very-important-function or nothing.
Examples: |
> (require pollen/top) | | > (module vif racket/base | (define (very-important-function . xs) `(secrets-of-universe ,@xs))) |
| | ; Forgot to (require 'vif) | > ((def/c very-important-function) "Bonjour") | very-important-function: undefined; | cannot reference undefined identifier |
|