[icfp] vector-sizes
This commit is contained in:
parent
a5d63661a6
commit
f5e514f257
|
@ -24,6 +24,12 @@ An important component of Typed Racket's design is that all macros in a program
|
||||||
This protocol lets us implement our elaborators as macros that expand into
|
This protocol lets us implement our elaborators as macros that expand into
|
||||||
typed code.
|
typed code.
|
||||||
|
|
||||||
|
Each elaborator is defined as a @emph{local} transformation on syntax.
|
||||||
|
Code produced by an elaboration is associated with compile-time data that
|
||||||
|
other elaborators may access.
|
||||||
|
In this way, we handle binding forms and propagate information through
|
||||||
|
@emph{unrelated} program transformations.
|
||||||
|
|
||||||
@parag{Conventions}
|
@parag{Conventions}
|
||||||
@itemlist[
|
@itemlist[
|
||||||
@item{
|
@item{
|
||||||
|
@ -45,21 +51,10 @@ 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)].
|
||||||
} @item{
|
|
||||||
TODO phantom item for space
|
|
||||||
TODO phantom item for space
|
|
||||||
TODO phantom item for space
|
|
||||||
TODO phantom item for space
|
|
||||||
TODO phantom item for space
|
|
||||||
} @item{
|
} @item{
|
||||||
In @Secref{sec:sql}, @tt{sql} is short for @tt{postgresql}, i.e.
|
In @Secref{sec:sql}, @tt{sql} is short for @tt{postgresql}, i.e.
|
||||||
the code we present in that section is only implemented for the @tt{postgresql}
|
the code we present in that section is only implemented for the @tt{postgresql}
|
||||||
interface.
|
interface.
|
||||||
} @item{
|
|
||||||
Until @Secref{sec:def-overview}, we use @racket[define] and @racket[let] forms sparingly.
|
|
||||||
In fact, this practice aligns with our implementation---once the interpretations
|
|
||||||
and elaborations are defined, extending them to handle arbitrary definitions
|
|
||||||
and renamings is purely mechanical.
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -287,107 +282,85 @@ On the other hand, if we modified @racket[cite] to take a third argument
|
||||||
@; =============================================================================
|
@; =============================================================================
|
||||||
@section{Numeric Constants}
|
@section{Numeric Constants}
|
||||||
|
|
||||||
The identity interpretation is useful for lifting constant values to
|
The identity interpretation @exact{$\RktMeta{id} \in \interp$}
|
||||||
the compile-time environment.
|
lifts values to the elaboration environment.
|
||||||
@racket[id] @exact|{$\in \interp$}|
|
When composed with a filter, we can recognize types of compile-time constants.
|
||||||
|
|
||||||
|
@todo{fbox id-int = int}
|
||||||
@racketblock[
|
@racketblock[
|
||||||
> (id 2)
|
> (int? 2)
|
||||||
2
|
2
|
||||||
> (id "~s")
|
> (int? "~s")
|
||||||
"~s"
|
|
||||||
]
|
|
||||||
|
|
||||||
When composed with a filter, we can recognize classes of constants.
|
|
||||||
That is, if @racket[int?] is the identity function on integer values
|
|
||||||
and the constant function returning @racket[#false] otherwise, we have
|
|
||||||
(@racket[int?]@exact{$\,\circ\,$}@racket[id])@exact|{$\,\in \interp$}|
|
|
||||||
|
|
||||||
@racketblock[
|
|
||||||
> (int? (id 2))
|
|
||||||
2
|
|
||||||
> (int? (id "~s"))
|
|
||||||
#false
|
#false
|
||||||
]
|
]
|
||||||
|
|
||||||
Using this technique we can detect division by the constant zero and
|
Constant-folding versions of arithmetic operators are now easy to define
|
||||||
implement constant-folding transformation of arithmetic operations.
|
in terms of the built-in operations.
|
||||||
@racket[t-arith] @exact{$\in \interp$}
|
Our implementation re-uses a single fold/elaborate loop to make
|
||||||
|
textualist wrappers over @racket[+], @racket[expt] and others.
|
||||||
|
|
||||||
|
@todo{fbox}
|
||||||
|
@racketblock[
|
||||||
|
> (define a 3)
|
||||||
|
> (define b (/ 1 (- a a)))
|
||||||
|
Error: division by zero
|
||||||
|
]
|
||||||
|
|
||||||
|
Partial folds also work as expected.
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
> (t-arith '(/ 1 0))
|
> (* 2 3 7 z)
|
||||||
⊥
|
|
||||||
> (t-arith '(* 2 3 7 z))
|
|
||||||
'(* 42 z)
|
'(* 42 z)
|
||||||
]
|
]
|
||||||
|
|
||||||
These transformations are most effective when applied bottom-up, from the
|
Taken alone, this re-implementation of constant folding in an earlier compiler
|
||||||
leaves of a syntax tree to the root.
|
stage is not terribly exciting.
|
||||||
|
But our arithmetic elaborators cooperate with other elaborators, for example
|
||||||
@racketblock[
|
size-aware vector operations.
|
||||||
> (t-arith '(/ 1 (- 5 5)))
|
|
||||||
⊥
|
|
||||||
]
|
|
||||||
|
|
||||||
@Secref{sec:implementation} describes how we accomplish this recursion scheme
|
|
||||||
with the Racket macro system.
|
|
||||||
@; The implementation of @racket[t-arith] expects flat/local data.
|
|
||||||
|
|
||||||
|
|
||||||
@; =============================================================================
|
@; =============================================================================
|
||||||
@section[#:tag "sec:vector"]{Sized Data Structures}
|
@section[#:tag "sec:vector"]{Fixed-Length Structures}
|
||||||
|
|
||||||
Vector bounds errors are always frustrating to debug, especially since
|
Fixed-length data structures are often initialized with a constant or
|
||||||
their cause is rarely deep.
|
computed-constant length.
|
||||||
In many cases, vectors are declared with a constant size and
|
Racket's vectors are one such structure.
|
||||||
accessed at constant positions, plus or minus an offset.@note{Loops do not
|
For each built-in vector constructor, we thus define an interpretation:
|
||||||
fit this pattern, but many languages already provide for/each constructs
|
|
||||||
to streamline common looping patterns.}
|
|
||||||
But unless the compiler or runtime system tracks vector bounds, the programmer
|
|
||||||
must unfold definitions and manually find the source of the problem.
|
|
||||||
|
|
||||||
A second, more compelling reason to track vector bounds is to remove the
|
|
||||||
run-time checks inserted by memory-safe languages.
|
|
||||||
If we can statically prove that a reference is in-bounds, we should be able
|
|
||||||
to optimize it.
|
|
||||||
|
|
||||||
Racket provides a few ways to create vectors.
|
|
||||||
Length information is explicit in some and derivable for
|
|
||||||
others, provided we interpret sub-expressions recursively.
|
|
||||||
@racket[vector->length] @exact{$\in \interp$}
|
|
||||||
|
|
||||||
|
@todo{fbox vector->size in interp}
|
||||||
@racketblock[
|
@racketblock[
|
||||||
> (vector->length '#(0 1 2 3))
|
> (vector->size '#(0 1 2))
|
||||||
3
|
3
|
||||||
> (vector->length '(make-vector 100 #true))
|
> (vector->size '(make-vector 100))
|
||||||
100
|
100
|
||||||
> (vector->length '(vector-append #(left) #(right)))
|
> (vector->size '(make-vector (/ 12 3)))
|
||||||
7
|
4
|
||||||
> (vector->length '(vector-drop #(A B C) 2))
|
|
||||||
1
|
|
||||||
]
|
]
|
||||||
|
|
||||||
Once we have sizes associated with compile-time vectors,
|
After interpreting, we associate the size with the new vector at compile-time.
|
||||||
we can define optimized translations of built-in functions.
|
Other elaborators can use and propagate these sizes; for instance, we have
|
||||||
For the following, assume that @racket[unsafe-ref] is an unsafe version of @racket[vector-ref].
|
implemented elaborating layers for thirteen standard vector operations.
|
||||||
@racket[t-vec] @exact{$\in \trans$}
|
Together, they constitute a length-aware vector library that serves as a
|
||||||
|
drop-in replacement for existing code.
|
||||||
|
If size information is missing, the operators default to Typed Racket's behavior.
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
> (t-vec '(vector-ref #(Y E S) 2))
|
> (vector-ref (make-vector 3) 4)
|
||||||
'(unsafe-ref #(Y E S) 2)
|
|
||||||
> (t-vec '(vector-ref #(N O) 2))
|
|
||||||
⊥
|
⊥
|
||||||
> (t-vec '(vector-length (make-vector 100 #true)))
|
> (vector-length (vector-append '#(A B) '#(C D)))
|
||||||
100
|
4
|
||||||
|
> (vector-ref (vector-map add1 '#(3 3 3)) 4)
|
||||||
|
(unsafe-ref (vector-map add1 '#(3 3 3)) 4)
|
||||||
]
|
]
|
||||||
|
|
||||||
We can define vectorized arithmetic using similar translations.
|
For the most part, these elaborations simply manage sizes and
|
||||||
Each operation match the length of its arguments at compile-time and expand
|
delegate the main work to Typed Racket vector operations.
|
||||||
to an efficient implementation.
|
We do, however, optimize vector references to unsafe primitives and
|
||||||
|
specialize operations like @racket[vector-length], as shown above.
|
||||||
|
|
||||||
|
|
||||||
@; =============================================================================
|
@; =============================================================================
|
||||||
@section[#:tag "sec:sql"]{Database Queries}
|
@section[#:tag "sec:sql"]{Database Schema}
|
||||||
@; db
|
@; db
|
||||||
@; TODO Ocaml, Haskell story
|
@; TODO Ocaml, Haskell story
|
||||||
@; TODO connect to ew-haskell-2012 os-icfp-2008
|
@; TODO connect to ew-haskell-2012 os-icfp-2008
|
||||||
|
|
Loading…
Reference in New Issue
Block a user