[icfp] impl part II, again

This commit is contained in:
ben 2016-03-16 04:32:26 -04:00
parent 0250b7b109
commit b50773ee46
3 changed files with 158 additions and 195 deletions

View File

@ -1,7 +1,7 @@
\begin{center} \begin{center}
\begin{tabular}{l l l} \begin{tabular}{l l l}
Syntax Class & Purpose \\\hline Syntax Class & Purpose \\\hline
\mod{fun/arity} & Infer function arity \\ \mod{fun/domain} & Infer function domain types \\
\mod{num/value} & Evaluate a numeric expression \\ \mod{num/value} & Evaluate a numeric expression \\
\mod{pattern/groups} & Count regexp groups \\ \mod{pattern/groups} & Count regexp groups \\
\mod{query/constr} & Parse \mod{SQL} queries \\ \mod{query/constr} & Parse \mod{SQL} queries \\

View File

@ -1,7 +1,7 @@
#lang scribble/sigplan #lang scribble/sigplan
@require["common.rkt"] @require["common.rkt"]
@title[#:tag "sec:implementation"]{Implementation} @title[#:tag "sec:implementation"]{More than a Pretty Face}
@; Amazing Macros @; Amazing Macros
@; Whirlwind tour @; Whirlwind tour
@; Accounting, taking stock @; Accounting, taking stock
@ -49,12 +49,13 @@ Much of the brevity is due to amortizing helper functions, so we include helpers
@; ----------------------------------------------------------------------------- @; -----------------------------------------------------------------------------
@section[#:tag "sec:impl-trans"]{Implementing Transformations} @; TODO not a great name @section[#:tag "sec:impl-elab"]{Elegant Elaborations}
@; @section{Ode to Macros: The Long Version}
At this point, we have carried on long enough talking about the implementation Our elaboration for @racket[vector-length] is straightforward.
without actually showing any code. If called with a size-annotated vector @racket[v], @racket[(vector-length v)]
No longer---here is our definition of @racket[vector-length]: elaborates to the size.
Otherwise, it defaults to Typed Racket's @racket[vector-length].
The implementation is equally concise, modulo some notation.
@codeblock{ @codeblock{
(make-alias #'vector-length (make-alias #'vector-length
@ -64,18 +65,14 @@ No longer---here is our definition of @racket[vector-length]:
[_ #false])) [_ #false]))
} }
First of all, this transformation works as specified in @Secref{sec:vector}.
When the length of its argument is known, it expands to that length.
Otherwise, it expands to an ordinary call to @racket[vector-length].
Second, we need to introduce a few mysterious characters:
@itemlist[ @itemlist[
@item{ @item{
@racket[(make-alias id f)] creates a transformation from an identifier @racket[id] @racket[(make-alias id f)] creates a elaboration from an identifier @racket[id]
and a partial function @racket[f]. and a partial function @racket[f].
} }
@item{ @item{
The symbol @tt{#'} creates a syntax object from a value or template. The symbol @exact|{\RktMeta{\#'}}| creates a syntax object from a value or template.
} }
@item{ @item{
A @racket[syntax-parser] is a match statement over syntactic patterns. A @racket[syntax-parser] is a match statement over syntactic patterns.
@ -84,41 +81,41 @@ Second, we need to introduce a few mysterious characters:
wildcard @tt{_}. wildcard @tt{_}.
} }
@item{ @item{
The colon character (@tt{:}) used int @racket[v:vector/length] The colon character (@tt{:}) used in @racket[v:vector/length]
binds the variable @racket[v] to the @emph{syntax class} @racket[vector/length]. binds the variable @racket[v] to the @emph{syntax class} @racket[vector/length].
} }
@item{ @item{
The dot character (@tt{.}) accesses an @emph{attribute} of the value bound The dot character (@tt{.}) accesses an @emph{attribute} of the value bound
to @racket[v]. to @racket[v].
In this case, the attribute @racket[evidence] is set when In this case, the attribute @racket[evidence] is set when the class
@racket[vector/length] matches successfully. @racket[vector/length] successfully matches the value of @racket[v].
} }
] ]
Third, we remark that the pattern @racket[v:vector/length] unfolds all The pattern @racket[v:vector/length] unfolds all
transformations to @racket[v] recursively. elaborations to @racket[v] recursively.
So we handle each of the following cases, as well as any other combination of So, as hinted in @Secref{sec:vector},
we handle each of the following cases as well as any other combination of
length-preserving vector operations. length-preserving vector operations.
@racketblock[ @racketblock[
> '(vector-length #(H I)) > (vector-length #(0 1 2))
2 2
> '(vector-length (vector-append #(Y O) > (vector-length (vector-append #(A B)
#(L O))) #(C D)))
4 4
] ]
@; The general features are explained in greater deteail below @; The general features are explained in greater deteail below
@; make-alias @; make-alias
@; TODO variable name for f The structure of @racket[vector-length] is common to many of our elaborations:
The overall structure of @racket[vector-length] is common to many of our transformations. we define a rule to handle an interesting syntactic pattern and
That is, we define a rule to handle an interesting syntactic pattern and
then generate an alias from the rule using the helper function @racket[make-alias]. then generate an alias from the rule using the helper function @racket[make-alias].
@codeblock{ @codeblock{
(define ((make-alias orig-id f) stx) (define ((make-alias orig-id elaborate) stx)
(or (f stx) (or (elaborate stx)
(syntax-parse stx (syntax-parse stx
[_:id [_:id
orig-id] orig-id]
@ -126,11 +123,10 @@ That is, we define a rule to handle an interesting syntactic pattern and
#`(#,orig-id e* ...)]))) #`(#,orig-id e* ...)])))
} }
The transformation defined by @racket[(make-alias id f)] is a function on The elaboration defined by @racket[(make-alias id elaborate)] is a function on
syntax objects. syntax objects.
First, the function applies @racket[f] to the syntax object @racket[stx]. This function first applies @racket[elaborate] to the syntax object @racket[stx].
If the result is not If the result is not @racket[#false] we return.
@racket[#false] we return.
Otherwise the function matches its argument against two possible patterns: Otherwise the function matches its argument against two possible patterns:
@itemize[ @itemize[
@item{ @item{
@ -141,29 +137,29 @@ Otherwise the function matches its argument against two possible patterns:
@racket[(_ e* ...)] matches function application. @racket[(_ e* ...)] matches function application.
In the result of this branch, In the result of this branch,
@; TODO backtick not printing right @; TODO backtick not printing right
we declare a syntax template with @tt{#`} and splice the identifier we declare a syntax template with @exact|{\RktMeta{\#`}}| and splice the identifier
@racket[orig-id] into the template with @tt{#,}. @racket[orig-id] into the template with @exact|{\RktMeta{,\#}}|.
These operators are formally known as @racket[quasisyntax] and @racket[unsyntax]; These operators are formally known as @racket[quasisyntax] and @racket[unsyntax];
you may know their cousins @racket[quasiquote] and @racket[unquote]. you may know their cousins @racket[quasiquote] and @racket[unquote].
} }
] ]
@emph{Note:} the identifier @racket[...] is not pseudocode! The identifier @racket[...] is not pseudocode.
In a pattern, it captures zero-or-more repetitions of the preceding pattern---in In a pattern, it captures zero-or-more repetitions of the preceding pattern---in
this case, the variable @racket[e*] binds anything so @racket[(_ e* ...)] matches this case, the variable @racket[e*] binds anything so @racket[(_ e* ...)] matches
lists with at least one element.@note{The name @racket[e*] is our own convention.} lists with at least one element.@note{The variable name @racket[e*] is our own convention.}
All but the first element of such a list is then bound to the identifier All but the first element of such a list is then bound to the identifier
@racket[e*] in the result. @racket[e*] in the result.
We use @racket[...] in the result to flatten the contents of @racket[e*] into We use @racket[...] in the result to flatten the contents of @racket[e*] into
the final expression. the final expression.
One last example transformation using @racket[make-alias] A second example using @racket[make-alias]
is our definition of @racket[vector-ref], shown below. is @racket[vector-ref] @exact{$\in \elab$}, shown below.
When given a sized vector @racket[v] and an expression @racket[e] that When given a sized vector @racket[v] and an expression @racket[e] that
expands to a number @racket[i], the function asserts that @racket[i] is expands to a number @racket[i], the function asserts that @racket[i] is
in bounds. in bounds.
If either @racket[vector/length] or @racket[expr->num] fail to coerce numeric If either @racket[vector/length] or @racket[expr->num] fail to coerce numeric
values, the function returns @racket[#false]. values, the function defaults to Typed Racket's @racket[vector-ref].
@codeblock{ @codeblock{
(make-alias #'vector-ref (make-alias #'vector-ref
@ -178,12 +174,12 @@ If either @racket[vector/length] or @racket[expr->num] fail to coerce numeric
[_ #false])) [_ #false]))
} }
Unlike the previous two functions, our @racket[vector-ref] transformation This elaboration
does more than just matching a pattern and returning a new syntax object. does more than just matching a pattern and returning a new syntax object.
Crucially, it compares the @emph{value} used to index its argument vector with Crucially, it compares the @emph{value} used to index its argument vector with
that vector's length before choosing how to expand. that vector's length before choosing how to expand.
To access these integer values outside of a template, we lift the pattern variables To access these integer values outside of a template, we lift the pattern variables
@racket[v] and @racket[e] to syntax objects with a @tt{#'} prefix. @racket[v] and @racket[e] to syntax objects with a @exact|{\RktMeta{\#'}}| prefix.
A helper function @racket[expr->num] then fully expands the syntax object @racket[#'e] A helper function @racket[expr->num] then fully expands the syntax object @racket[#'e]
and the built-in @racket[syntax->datum] gets the integer value stored at the and the built-in @racket[syntax->datum] gets the integer value stored at the
attribute @racket[#'v.evidence]. attribute @racket[#'v.evidence].
@ -195,96 +191,102 @@ The interesting design challenge is making one pattern that covers all
@; ============================================================================= @; =============================================================================
@section[#:tag "sec:impl-interp"]{Implementing Interpretations} @; TODO a decidedly bad name @section[#:tag "sec:impl-interp"]{Illustrative Interpretations}
By now we have seen two useful syntax classes: @racket[id] and @racket[vector/length]. By now we have seen two useful syntax classes: @racket[id] and
@racket[vector/length].@note{The name @racket[vector/length] should
be read as ``vector @emph{with} length information''.}
In fact, we use syntax classes as the front-end for each function in @exact{$\interp$}. In fact, we use syntax classes as the front-end for each function in @exact{$\interp$}.
@Figure-ref{fig:stxclass} lists all of our syntax classes and ties each to a purpose @Figure-ref{fig:stxclass} lists all of our syntax classes and ties each to a purpose
motivated in @Secref{sec:usage}.@note{The name @racket[vector/length] should motivated in @Secref{sec:usage}.
be read as ``vector @emph{with} length information''.}
@figure["fig:stxclass" @figure["fig:stxclass"
"Registry of syntax classes" "Registry of syntax classes"
@exact|{\input{fig-stxclass}}| @exact|{\input{fig-stxclass}}|
] ]
These classes are implemented uniformly from predicates on syntax objects. The definitions of these classes are generated from predicates on syntax
One such predicate is @racket[arity?], shown below, which counts objects.
the parameters accepted by an uncurried anonymous function and returns One such predicate is @racket[vector?], shown below, which counts the
@racket[#false] for all other inputs. length of vector values and returns @racket[#false] for all other inputs.
Notice that the pattern for @racket[make-vector] recursively expands its
first argument using the @racket[num/value] syntax class.
@codeblock{ @codeblock{
(define arity? (define vector?
(syntax-parser #:literals (λ) (syntax-parser #:literals (make-vector)
[(λ (x*:id ...) e* ...) [#(e* ...)
(length (syntax->datum #'(x* ...)))] (length (syntax->datum #'(e* ...)))]
[(make-vector n:num/value)
(syntax->datum #'n.evidence)]
[_ #f])) [_ #f]))
} }
The syntax class @racket[procedure/arity] is then defined as ...
@racketblock[ From @racket[vector?], we define the syntax class @racket[vector/length]
> (define-stxclass/pred procedure/arity that handles the mechanical work of macro-expanding its input,
arity?) applying the @racket[vector?] predicate, and caching results in the
] @racket[evidence] and @racket[expanded] attributes.
... in terms of another macro, which handles the routine work of recursively
expanding its input, applying the @racket[arity?] predicate,
and caching results in the @racket[evidence] and @racket[expanded] attributes.
@codeblock{ @codeblock{
(define-syntax-rule (define-stxclass/pred id p?) (define-syntax-class vector/length
(define-syntax-class id
#:attributes (evidence expanded) #:attributes (evidence expanded)
(pattern e (pattern e
#:with e+ (expand-expr #'e) #:with e+ (expand-expr #'e)
#:with p+ (p? #'e+) #:with len (vector? #'e+)
#:when (syntax->datum #'p+) #:when (syntax->datum #'len)
#:attr evidence #'p+ #:attr evidence #'len
#:attr expanded #'e+))) #:attr expanded #'e+))
} }
A @racket[define-syntax-rule] is an inlined definition; using it here does not The @racket[#:attributes] declaration is key.
save any space, but in practice we re-use the same alias for each of
our custom syntax classes.
The @racket[#:attributes] declaration is very important.
This is where the earlier-mentioned @racket[v.expanded] and @racket[v.evidence] This is where the earlier-mentioned @racket[v.expanded] and @racket[v.evidence]
were defined, and indeed these two attributes form the backbone of our value-parsing properties are defined, and indeed these two attributes form the backbone
protocol. of our protocol for cooperative elaborations.
In terms of a pattern @racket[x:procedure/arity], their meaning is: In terms of a pattern @racket[v:vector/length], their meaning is:
@itemlist[ @itemlist[
@item{ @item{
@racket[x.expanded] is the result of fully expanding all macros and transformations @racket[v.expanded] is the result of fully expanding all macros and elaborations
contained in the syntax object bound to @racket[x]. contained in the syntax object bound to @racket[v].
The helper function @racket[expand-expr] triggers this depth-first expansion. The helper function @racket[expand-expr] triggers this depth-first expansion.
} }
@item{ @item{
@racket[x.evidence] is the result of applying the @racket[arity?] predicate @racket[v.evidence] is the result of applying the @racket[vector?] predicate
to the expanded version of @racket[x]. to the expanded version of @racket[v].
Intuitively, @racket[x.evidence] is the reason why we should be able to In general, @racket[v.evidence] is the reason why we should be able to
perform transformations using @racket[x]. perform elaborations using the value bound to @racket[v].
} }
] ]
If the predicate @racket[arity?] returns @racket[#false], then the boolean If the predicate @racket[vector?] returns @racket[#false] then the boolean
@racket[#:when] guard fails because the value contained in the syntax object @racket[#:when] guard fails because the value contained in the syntax object
@racket[p+] will be @racket[#false]. @racket[len] will be @racket[#false].
When this happens, neither attribute is bound and the pattern When this happens, neither attribute is bound and the pattern
@racket[x.procedure/arity] will fail. @racket[v.vector/length] will fail.
@; ============================================================================= @; =============================================================================
@section{Implementing Definitions} @section{Automatically Handling Variables}
With that, we have essentially finished our tour of the key ideas underlying When the results of an elaboration are bound to a variable @racket[v],
our implementation. we frequently need to associate a compile-time value to @racket[v] for
The one detail we elided is precisely how interpreted data is propogated upward later elaborations to use.
through recursive transformations, especially since transformations may unfold This is often the case for calls to @racket[sql-connect]:
into arbitrary, difficult-to-parse code.
An illustrative example is our transformation for @racket[sql-connect], @racketblock[
the library function for connecting a user to a database. (define C (sql-connect ....))
Recall that our library imposes an extra constraint on calls to @racket[sql-connect]: (query-row C ....)
they must supply a database schema, which is erased in translation. ]
Reading the literal variable @racket[C] gives no useful information
when elaborating the call to @racket[query-row].
Instead, we need to retrieve the database schema for the connection bound to @racket[C].
The solution starts with our implementation of @racket[sql-connect],
which uses the built-in function
@racket[syntax-property] to associate a key/value pair with a syntax object.
Future elaborations on the syntax object @racket[#'(sql-connect e* ...)] can
retrieve the database schema @racket[#'s.evidence] by using the key @racket[connection-key].
@racketblock[ @racketblock[
(syntax-parser (syntax-parser
@ -297,45 +299,16 @@ Recall that our library imposes an extra constraint on calls to @racket[sql-conn
"Missing schema")]) "Missing schema")])
] ]
Most of this definition is routine. Storing this syntax property is the job of the programmer, but we automate
We use the syntax class @racket[schema/spec] to lift schema specifications to the task of bubbling the property up through variable definitions by overriding
the compile-time environment and we ultimately forward all non-schema arguments Typed Racket's @racket[define] and @racket[let] forms.
to the default @racket[sql-connect].@note{If an one of the arguments New definitions search for specially-keyed properties like @racket[connection-key];
@racket[e* ...] is malformed, this will be reported by the original when found, they associate their variable with the property in a local hashtable
@racket[sql-connect]. Three cheers for division of labor!} whose keys are @exact{$\alpha$}-equivalence classes of identifiers.
The new form is @racket[syntax-property], which tags our new syntax object New @racket[let] bindings work similarly, but redirect variable references
with a key/value pair. within their scope.
Here the key is @racket[connection-key], which we generate when compiling a file The technical tools for implementing these associations are @racket[free-id-table]s
and use to identify connection objects. and @racket[rename-transformer]s (@Secref{sec:rename}).
The value is the evidence parsed from the schema description.
Transformation writers must take care to install @racket[syntax-property]
information, but we automate the task of retrieving cached properties
in our syntax classes---before
applying a predicate, we first search for a cached value.
Syntax properties are likewise the trick for propagating metadata through
@racket[let] and @racket[define] bindings.
The technical tools for this are @racket[rename-transformer]s and @racket[free-id-table]s,
which we discuss in @Secref{sec:rename}.
@;@codeblock{
@; (make-alias #'vector-append
@; (syntax-parser
@; [(_ v0:vector/length v1:vector/length)
@; (define len0 (syntax-e #'v1.evidence))
@; (define len1 (syntax-e #'v2.evidence))
@; (define new-len (+ len0 len1))
@; (syntax-property
@; #`(build-vector
@; #,new-len
@; (lambda (i)
@; (if (< i '#,len0)
@; (unsafe-vector-ref v1.expanded i)
@; (unsafe-vector-ref v2.expanded i)))
@; vector-length-key
@; new-len))]
@; [_ #f]))
@;}
@; ----------------------------------------------------------------------------- @; -----------------------------------------------------------------------------
@ -343,20 +316,19 @@ The technical tools for this are @racket[rename-transformer]s and @racket[free-i
@; Symphony of features @; Symphony of features
Whereas the previous section was a code-first tour of key techniques supporting This section is a checklist of important meta-programming
our implementation, this section is a checklist of important meta-programming
tools provided by the Racket macro system. tools provided by the Racket macro system.
For ease of reference, our discussion proceeds from the most useful tool to Each sub-section title is a technical term;
for ease of reference, our discussion proceeds from the most useful tool to
the least. the least.
Each sub-section title is a technical term.
@;Titles marked with an asterisk are essential to our implementation, @;Titles marked with an asterisk are essential to our implementation,
@; others could be omitted without losing the essence. @; others could be omitted without losing the essence.
@subsection[#:tag "sec:parse"]{Syntax Parse} @subsection[#:tag "sec:parse"]{Syntax Parse}
The @racket[syntax/parse] library@~cite[c-dissertation-2010] is a powerful The @racket[syntax/parse] library@~cite[c-dissertation-2010] provides
interface for writing macros. tools for writing macros.
It provides the @racket[syntax-parse] and @racket[syntax-parser] forms that It provides the @racket[syntax-parse] and @racket[syntax-parser] forms that
we have used extensively above. we have used extensively above.
From our perspective, the key features are: From our perspective, the key features are:
@ -364,13 +336,13 @@ From our perspective, the key features are:
@item{ @item{
A rich pattern-matching language; including, for example, A rich pattern-matching language; including, for example,
repetition via @racket[...], @racket[#:when] guards, and matching repetition via @racket[...], @racket[#:when] guards, and matching
for identifiers like @racket[λ] (top of @Secref{sec:impl-interp}) for identifiers like @racket[make-vector] (top of @Secref{sec:impl-interp})
that respects @exact{$\alpha$}-equivalence. that respects @exact{$\alpha$}-equivalence.
} }
@item{ @item{
Freedom to mix arbitrary code between the pattern spec and result, Freedom to mix arbitrary code between the pattern spec and result,
as shown in the definition of @racket[vector-ref] as shown in the definition of @racket[vector-ref]
(bottom of @Secref{sec:impl-trans}). (bottom of @Secref{sec:impl-elab}).
} }
] ]
@ -382,12 +354,12 @@ Racket's macro expander normally proceeds in a breadth-first manner, traversing
After expansion, sub-trees are traversed and expanded. After expansion, sub-trees are traversed and expanded.
This ``lazy'' sort of evaluation is normally useful because it lets macro This ``lazy'' sort of evaluation is normally useful because it lets macro
writers specify source code patterns instead of forcing them to reason about writers specify source code patterns instead of forcing them to reason about
the shape of expanded code. the syntax trees of expanded code.
Our transformations, however, are most effective when value information is Our elaborations, however, are most effective when value information is
propogated bottom up from macro-free syntactic values through other combinators. propogated bottom up from macro-free syntactic values through other combinators.
This requires depth-first macro expansion; for instance, in the first argument This requires depth-first macro expansion; for instance, in the first argument
of the @racket[vector-ref] transformation defined in @Secref{sec:impl-trans}. of the @racket[vector-ref] elaboration defined in @Secref{sec:impl-elab}.
Fortunately, we always know which positions to expand depth-first and Fortunately, we always know which positions to expand depth-first and
Racket provides a function @racket[local-expand] that will fully expand Racket provides a function @racket[local-expand] that will fully expand
a target syntax object. a target syntax object.
@ -399,15 +371,21 @@ In particular, all our syntax classes listed in @Figure-ref{fig:stxclass}
A syntax class encapsulates common parts of a syntax pattern. A syntax class encapsulates common parts of a syntax pattern.
With the syntax class shown at the end of @Secref{sec:impl-interp} With the syntax class shown at the end of @Secref{sec:impl-interp}
we save 2-6 lines of code in each of our transformation functions. we save 2-6 lines of code in each of our elaboration functions.
More importantly, syntax classes provide a clean implementation of the ideas More importantly, syntax classes provide a clean implementation of the ideas
in @Secref{sec:solution}. in @Secref{sec:solution}.
Given a function in @exact{$\interp$} that extracts data from core value/expression Given a function in @exact{$\interp$} that extracts data from core value/expression
forms, we generate a syntax class that applies the function and handles forms, we generate a syntax class that applies the function and handles
intermediate binding forms. intermediate binding forms.
Functions in @exact{$\trans$} can branch on whether the syntax class matched Functions in @exact{$\elab$} can branch on whether the syntax class matched
instead of parsing data from program syntax. instead of parsing data from program syntax.
In other words, syntax classes provide an interface that lets us reason
locally when writing elaborators.
The only question we ask during elaboration is whether a syntax object is associated
with an interpreted value---not how the object looks or what sequence of
renamings it filtered through.
@subsection[#:tag "sec:idmacro"]{Identifier Macros} @subsection[#:tag "sec:idmacro"]{Identifier Macros}
@ -424,14 +402,14 @@ or: bad syntax
Identifier macros are allowed in both higher-order and top-level positions, Identifier macros are allowed in both higher-order and top-level positions,
just like first-class functions. just like first-class functions.
This lets us transparently alias built-in functions like @racket[regexp-match] This lets us transparently alias built-in functions like @racket[regexp-match]
and @racket[vector-length] (see @Secref{sec:impl-trans}). and @racket[vector-length] (see @Secref{sec:impl-elab}).
The higher-order uses cannot be checked for bugs, but they execute as normal The higher-order uses cannot be checked for bugs, but they execute as normal
without raising new syntax errors. without raising new syntax errors.
@subsection[#:tag "sec:def-implementation"]{Syntax Properties} @subsection[#:tag "sec:def-implementation"]{Syntax Properties}
Syntax properties are the glue that let us chain transformations together. Syntax properties are the glue that let us compose elaborations.
For instance, @racket[vector-map] preserves the length of its argument vector. For instance, @racket[vector-map] preserves the length of its argument vector.
By tagging calls to @racket[vector-map] with a syntax property, our system By tagging calls to @racket[vector-map] with a syntax property, our system
becomes aware of identities like: becomes aware of identities like:
@ -452,21 +430,6 @@ This proved useful in our implementation of @racket[query-row], where we stored
Cooperating with @racket[let] and @racket[define] bindings is an important Cooperating with @racket[let] and @racket[define] bindings is an important
usability concern. usability concern.
When testing this library on existing code, we often saw code like:
@(begin
#reader scribble/comment-reader
(racketblock
(define rx-email #rx"^(.*)@(.*)\\.(.*)$")
;; Other code here
(define (get-recipient str)
(regexp-match rx-email str))
))
Similarly for database code and arithmetic constants.
To deal with @racket[let] bindings, we use a @racket[rename-transformer]. To deal with @racket[let] bindings, we use a @racket[rename-transformer].
Within the binding's scope, the transformer redirects references from a variable Within the binding's scope, the transformer redirects references from a variable
to an arbitrary syntax object. to an arbitrary syntax object.
@ -482,17 +445,18 @@ For our purposes, we redirect to an annotated version of the same variable:
] ]
For definitions, we use a @emph{free-identifier table}. For definitions, we use a @emph{free-identifier table}.
This is less fancy--just a hashtable whose keys respect @exact{$\alpha$}-equivalence--but This is less fancy--just a hashtable whose keys respect
still useful in practice. @exact{$\alpha$}-equivalence--but still useful in practice.
@subsection[#:tag "sec:phase"]{Phasing} @subsection[#:tag "sec:phase"]{Phasing}
Any code between a @racket[syntax-parse] pattern and the output syntax object Any code between a @racket[syntax-parse] pattern and the output syntax object
is run at compile-time to generate the code that is ultimately run. is run at compile-time to generate the code that is ultimately run.
In general terms, the code used to directly generate run-time code In general terms, code used to directly generate run-time code
executes at @emph{phase level} 1 relative to the enclosing module. executes at @emph{phase level} 1 relative to the enclosing module.
Code used to generate a syntax-parse pattern may be run at phase level 2, and Code used to generate a @racket[syntax-parse] pattern may be run at
phase level 2, and
so on up to as many phases as needed@~cite[f-icfp-2002]. so on up to as many phases as needed@~cite[f-icfp-2002].
Phases are explicitly separated. Phases are explicitly separated.
@ -501,11 +465,9 @@ Also by design, it is very easy to import bindings from any module at a specific
phase. phase.
The upshot of this is that one can write and test ordinary, phase-0 Racket code The upshot of this is that one can write and test ordinary, phase-0 Racket code
but then use it at a higher phase level. but then use it at a higher phase level.
@; + non-meta programming We also have functions like @racket[+] available at whatever stage of
@; + not getting stuck in ascending ladder macro expansion we should need them---no need to copy and paste the implementation
@; + modular development at a different phase level@~cite[ew-haskell-2012].
@; + don't need to worry about Singletons Haskell duplication
@; There is no need to duplicate code for use at different phases.
@subsection{Lexical Scope, Source Locations} @subsection{Lexical Scope, Source Locations}
@ -513,7 +475,8 @@ The upshot of this is that one can write and test ordinary, phase-0 Racket code
Perhaps it goes without saying, but having macros that respect lexical scope Perhaps it goes without saying, but having macros that respect lexical scope
is important for a good user and developer experience. is important for a good user and developer experience.
Along the same lines, the ability to propogate source code locations in Along the same lines, the ability to propogate source code locations in
transformations lets us report syntax errors in terms of the programmer's elaborations lets us report syntax errors in terms of the programmer's
source code rather than locations inside our library. source code rather than locations inside our library.
Even though we may implement complex transformations, errors can always be
traced to a source code line number.

View File

@ -35,20 +35,19 @@ In this way, we handle binding forms and propagate information through
@item{ @item{
Interpretation and elaboration functions are defined over symbolic expressions Interpretation and elaboration functions are defined over symbolic expressions
and values; specifically, over @emph{syntax objects}. and values; specifically, over @emph{syntax objects}.
To make clear the difference between Typed Racket terms and representations To distinguish terms and syntax objects representing
of terms, we quote the latter and typeset it in green. terms, we quote the latter and typeset it in green.
Using this notation, @racket[(λ (x) x)] is a term implementing the identity For example, @racket[(λ (x) x)] is a term implementing the identity
function and @racket['(λ (x) x)] is a representation that will evaluate function and @racket['(λ (x) x)] is a representation that will evaluate
to the identity function. to the identity function.
Values are typeset in green because their syntax and term representations are identical. Values are typeset in @exact|{\RktVal{green}}| because their syntax and
term representations are identical.
} @item{ } @item{
In practice, syntax objects carry lexical information. Syntax objects carry lexical information, but our examples treat them as
Such information is extremely important, especially for implementing @racket[define] flat symbols.
and @racket[let] forms that respect @exact{$\alpha$}-equivalence, but
to simplify our presentation we omit it.
} @item{ } @item{
We use an infix @tt{:} to write explicit type annotations and casts, We use an infix @tt{::} to write explicit type annotations and casts,
for instance @racket[(x : Integer)]. for instance @racket[(x :: Integer)].
These normally have two different syntaxes, respectively These normally have two different syntaxes, respectively
@racket[(ann x Integer)] and @racket[(cast x Integer)]. @racket[(ann x Integer)] and @racket[(cast x Integer)].
} }
@ -122,7 +121,7 @@ For all other syntax patterns, @racket[t-printf] is the identity elaboration.
\RktSym{{\Stttextmore}}\mbox{\hphantom{\Scribtexttt{x}}}\RktPn{(}\RktSym{t{-}printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{{\textquotesingle}}\RktVal{(}\RktVal{printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"$\sim$b"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"2"}\RktVal{)}\RktPn{)} \RktSym{{\Stttextmore}}\mbox{\hphantom{\Scribtexttt{x}}}\RktPn{(}\RktSym{t{-}printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{{\textquotesingle}}\RktVal{(}\RktVal{printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"$\sim$b"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"2"}\RktVal{)}\RktPn{)}
\RktVal{{\textquotesingle}}\RktVal{(}\RktVal{printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"$\sim$b"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{(}\RktVal{"2"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{{\hbox{\texttt{:}}}}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{Integer}\RktVal{)}\RktVal{)} \RktVal{{\textquotesingle}}\RktVal{(}\RktVal{printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{"$\sim$b"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{(}\RktVal{"2"}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{{\hbox{\texttt{::}}}}\mbox{\hphantom{\Scribtexttt{x}}}\RktVal{Integer}\RktVal{)}\RktVal{)}
\RktSym{{\Stttextmore}}\mbox{\hphantom{\Scribtexttt{x}}}\RktPn{(}\RktSym{t{-}printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktSym{printf}\RktPn{)} \RktSym{{\Stttextmore}}\mbox{\hphantom{\Scribtexttt{x}}}\RktPn{(}\RktSym{t{-}printf}\mbox{\hphantom{\Scribtexttt{x}}}\RktSym{printf}\RktPn{)}
@ -219,8 +218,8 @@ It also raises syntax errors when an uncompiled regular expression contains
@racketblock[ @racketblock[
> (t-regexp '(regexp-match #rx"(a)b" str)) > (t-regexp '(regexp-match #rx"(a)b" str))
'(cast (regexp-match #rx"(a)b" str) '((regexp-match #rx"(a)b" str)
(U #f (List String String))) :: (U #f (List String String)))
> (t-regexp '(regexp-match "(" str)) > (t-regexp '(regexp-match "(" str))
] ]
@ -377,7 +376,7 @@ Arguments substituted for query parameters are guarded against SQL injection.
> (query-row C > (query-row C
"SELECT plaintiff FROM rulings WHERE name = '$1' LIMIT 1" "SELECT plaintiff FROM rulings WHERE name = '$1' LIMIT 1"
2001) 2001)
#("Kyllo") '#("Kyllo")
] ]
This is a far cry from language-integrated query@~cite[mbb-sigmod-2006] or This is a far cry from language-integrated query@~cite[mbb-sigmod-2006] or
@ -465,17 +464,17 @@ There is still a non-trivial amount of work to be done resolving wildcards and
> (t '(query-row C > (t '(query-row C
"SELECT plaintiff FROM decisions WHERE year = '$1' LIMIT 1" "SELECT plaintiff FROM decisions WHERE year = '$1' LIMIT 1"
2006)) 2006))
'(cast (query-row C '((query-row C
"SELECT ..." "SELECT ..."
(2006 : Natural)) (2006 : Natural))
(Vector String)) :: (Vector String))
> (t '(query-row C > (t '(query-row C
"SELECT * FROM decisions WHERE plaintiff = '$1' LIMIT 1" "SELECT * FROM decisions WHERE plaintiff = '$1' LIMIT 1"
"United States")) "United States"))
'(cast (query-row C '((query-row C
"SELECT ..." "SELECT ..."
("United States" : String)) ("United States" : String))
(Vector Natural String String Natural)) :: (Vector Natural String String Natural))
> (t '(query-row C "SELECT foo FROM decisions")) > (t '(query-row C "SELECT foo FROM decisions"))
] ]
@ -483,7 +482,8 @@ There is still a non-trivial amount of work to be done resolving wildcards and
Results produced by @racket[query-row] are vectors with a known length; Results produced by @racket[query-row] are vectors with a known length;
as such they cooperate with our library of vector operations described in as such they cooperate with our library of vector operations described in
@Secref{sec:vector}. @Secref{sec:vector}.
All these compose seamlessly, without any burden on the user. Accessing a constant index into a row vector is statically guaranteed
to be in bounds.
@; casts can fail, especially if you wildcard without saying all the rows @; casts can fail, especially if you wildcard without saying all the rows