[docs] polish guide, add more examples

This commit is contained in:
Stephen Chang 2016-09-30 11:10:23 -04:00
parent ffa0a05e26
commit 577f47f46a

View File

@ -80,19 +80,19 @@ The above code assumes some existing bindings:
@racket[λ-].}
]
A language defined with Turnstile must define or import the first two items
(we defer explaining how until later) while the last two items are available by
(see @secref{tycon}) while the last two items are available by
default in Turnstile.
A @racket[define-typed-syntax] form resembles a conventional Racket
macro-definition form: the above definitions begin with an input pattern, where
The @racket[define-typed-syntax] form resembles a conventional Racket
macro definition: the above definitions begin with an input pattern, where
the leftmost identifier is the name of the macro, followed by a @racket[≫]
literal, a series of premises, and finally a conclusion (the conclusion includes
literal, a series of premises, and finally a conclusion (which includes
the output @tech:stx-template).
The @racket[define-typed-syntax] is roughly an extension of
More specifically, @racket[define-typed-syntax] is roughly an extension of
@racket[define-syntax-parser] in that:
@itemlist[
@item{A programmer may specify @racket[syntax-parse]
@item{a programmer may specify @racket[syntax-parse]
options, e.g., @racket[#:datum-literals];}
@item{a pattern position may use any @racket[syntax-parse] combinators, e.g.
@racket[~and], @racket[~seq], or custom-defined @tech:pat-expanders;}
@ -101,26 +101,33 @@ The @racket[define-typed-syntax] is roughly an extension of
@subsection{Judgements vs @racket[define-typed-syntax]}
Compared to the type judgements in the @secref{judgements} section,
Turnstile @racket[define-typed-syntax] definitions differ in a few obvious ways:
@itemlist[
@item{Each premise and conclusion must be enclosed in brackets.}
@item{A conclusion is "split" into its inputs (at the top) and outputs (at the
bottom) to resemble other Racket macro-definition forms, enabling the
definition to be more easily read as code, top-to-bottom.
The @racket[define-typed-syntax] form extends typical Racket macros by allowing
interleaved type checking computations, written in type-judgement-like syntax,
directly in the macro definition.
Compared to the type judgements in the @secref{judgements} section, Turnstile
@racket[define-typed-syntax] definitions differ in a few obvious ways:
@itemlist[ @item{Each premise and conclusion must be enclosed in brackets.}
@item{A conclusion is "split" into its inputs (at the top) and outputs (at the
bottom) to resemble other Racket macro-definition forms. In other words,
pattern variable scope flows top-to-bottom, enabling the definition to be more
easily read as code.
For example, the conclusion input in the definition of @racket[λ] is the
initial input pattern @racket[(λ ([x:id : τ_in:type] ...) e)] and the
conclusion outputs are the output templates @racket[(λ- (x- ...) e-)] and
@racket[(→ τ_in.norm ... τ_out)]. The initial @racket[≫] separates the input
pattern from the premises while the @racket[⇒] in the conclusion associates
the type with the output expression.}
initial input pattern @racket[(λ ([x:id : τ_in:type] ...) e)] and the
conclusion outputs are the output templates @racket[(λ- (x- ...) e-)] and
@racket[(→ τ_in.norm ... τ_out)]. The initial @racket[≫] delimiter separates
the input pattern from the premises while the @racket[⇒] in the conclusion
associates the type with the output expression.}
@item{The rule implementations do not thread through an explicit type
environment @racket[Γ].
Rather, Turnstile reuses Racket's lexical scope as the type environment and
thus only new type environment bindings should be specified (to the left of
the @racket[⊢]), just like a @racket[let].}
@item{Since type environments use lexical scope, an explicit implementation of
@item{Since type environments merely follow Racket's lexical scoping, an explicit implementation of
the @tt{[VAR]} rule is unneeded.}]
@subsection{Premises}
@ -149,7 +156,9 @@ reported if the types do not match.
The @racket[λ] definition above also utilizes a @racket[⇒] premise, except it
must add bindings to the type environment, which are written to the left of the
@racket[⊢]. Observe how ellipses may be used in exactly the same manner as
@racket[⊢]. The lambda body is then type checked in this context.
Observe how ellipses may be used in exactly the same manner as
other Racket macros. (The @racket[norm] @tech:attribute comes from the
@racket[type] syntax class and is bound to the expanded representation of the
type. This is used to avoid double-expansions of the types.)
@ -173,11 +182,11 @@ more precise error for an arity mismatch:
--------
[⊢ (#%app- e_fn- e_arg- ...) ⇒ τ_out]))]
@section{Defining types}
@section[#:tag "tycon"]{Defining types}
We next extend our language with a type definition:
@label-code["The function type definition"
@label-code["The function type"
(racketblock0
(define-type-constructor → #:arity > 0))]
@ -188,11 +197,11 @@ easier to match on the type in @|tech:stx-pats|.
@section{Defining @racket[⇐] rules}
The astute reader has probably noticed that the type judgements from the
@secref{judgements} section are incomplete. Specifically, @\racket[⇐] clauses
The astute reader has probably noticed that the type judgements from
@secref{judgements} are incomplete. Specifically, @\racket[⇐] clauses
appear in the premises but never in the conclusion.
To complete the judgements, we add a general @racket[⇐] rule that
To complete the judgements, we need a general @racket[⇐] rule that
dispatches to the appropriate @racket[⇒] rule:
@verbatim|{
@ -203,7 +212,7 @@ dispatches to the appropriate @racket[⇒] rule:
}|
Turnstile similarly defines an implicit @racket[⇐] rule so programmers need not
specify a @racket[⇐] variant for every rule. A programmer may still specify an
specify a @racket[⇐] variant for every rule. A programmer may still write an
explicit @racket[⇐] rule if desired, however. This is especially useful for
implementing (local) type inference or type annotations. Here is an
extended lambda that additionally specifies a @racket[⇐] clause.
@ -279,7 +288,7 @@ here is whatever is in scope. By default it is Racket's @racket[+].
We can now write well-typed programs! Here is the complete
language implementation:
@label-code["A complete STLC implementation, created with #lang turnstile"
@label-code["STLC"
@#reader scribble/comment-reader
(racketblock0
#,(lang turnstile)
@ -326,28 +335,34 @@ language implementation:
[_ #:error (type-error #:src #'x
#:msg "Unsupported literal: ~v" #'x)]]))]
@(define the-eval
@(define stlc-eval
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-eval-limits #f])
(make-base-eval #:lang 'macrotypes/examples/stlc+lit )))
(make-base-eval #:lang 'turnstile/examples/stlc+lit )))
@(examples #:eval the-eval
(+ 1 2)
(((λ ([f : (→ Int Int Int)])
(λ ([x : Int][y : Int])
(f x y)))
+)
1 2)
(eval:error (+ 1 (λ ([x : Int]) x))))
@examples[#:eval stlc-eval
1
(eval:error "1")
(+ 1 2)
(eval:error (+ 1 (λ ([x : Int]) x)))
(eval:error (λ (x) x))
(ann (λ (x) x) : (→ Int Int))
((ann (λ (x) x) : (→ Int Int)) 1)
(((λ ([f : (→ Int Int Int)])
(λ ([x : Int][y : Int])
(f x y)))
+)
1 2)
]
@section{Extending a Language}
Imagine our language from the previous section is named @tt{STLC}. Since it
consists of just a series of macros, like any other Racket @hash-lang[], its forms may
be imported and exported and may be easily reused in other languages. Let's see
how we can reuse the above implementation to implement a subtyping language.
Since @tt{STLC} consists of just a series of macros, like any other Racket
@hash-lang[], its forms may be imported and exported and may be easily reused
in other languages. Let's see how we can reuse the above implementation to
implement a subtyping language.
@label-code["A language with subtyping that reuses STLC"
@(racketblock0 #:escape esc
@ -357,6 +372,7 @@ how we can reuse the above implementation to implement a subtyping language.
(define-base-types Top Num Nat)
(define-primop + : (→ Num Num Num))
(define-primop add1 : (→ Int Int))
(define-typed-syntax #%datum
[(_ . n:nat) ≫
@ -424,9 +440,29 @@ clause, using the @racket[≻] conclusion form, which dispatches to another
(typed) macro. In this manner, the new typed language may still define and invoke
macros like any other Racket program.
Since the new language uses subtyping, it also defines a @racket[join]
Since the new language uses subtyping, it also defines a (naive) @racket[join]
function, which is needed by conditional forms like @racket[if]. The
@racket[if] definition uses the @racket[current-join] parameter, to
make it reusable by other languages. Observe that the output type in the
@racket[if] definition uses @racket[unquote]. In general, all @tech:stx-template
positions in Turnstile are @racket[quasisyntax]es.
@(define stlc+sub-eval
(parameterize ([sandbox-output 'string]
[sandbox-error-output 'string]
[sandbox-eval-limits #f])
(make-base-eval #:lang 'turnstile/examples/stlc+sub)))
@examples[#:eval stlc+sub-eval
((λ ([x : Top]) x) -1)
((λ ([x : Num]) x) -1)
((λ ([x : Int]) x) -1)
(eval:error ((λ ([x : Nat]) x) -1))
((λ ([f : (→ Int Int)]) (f -1)) add1)
((λ ([f : (→ Nat Int)]) (f 1)) add1)
(eval:error ((λ ([f : (→ Num Int)]) (f 1.1)) add1))
((λ ([f : (→ Nat Num)]) (f 1)) add1)
(eval:error ((λ ([f : (→ Int Nat)]) (f 1)) add1))
(eval:error ((λ ([f : (→ Int Int)]) (f 1.1)) add1))
]