On this page:
2.1 Binding Forms
let:
plet:
letrec:
let*:
let-values:
letrec-values:
let*-values:
let/  cc:
let/  ec:
2.2 Anonymous Functions
lambda:
λ:
plambda:
case-lambda:
pcase-lambda:
opt-lambda:
popt-lambda:
2.3 Loops
for:
for/  list:
for/  hash:
for/  hasheq:
for/  hasheqv:
for/  vector:
for/  flvector:
for/  and:
for/  or:
for/  first:
for/  last:
for/  sum:
for/  product:
for*/  list:
for*/  hash:
for*/  hasheq:
for*/  hasheqv:
for*/  vector:
for*/  flvector:
for*/  and:
for*/  or:
for*/  first:
for*/  last:
for*/  sum:
for*/  product:
for/  lists:
for/  fold:
for*:
for*/  lists:
for*/  fold:
for
for*
do:
2.4 Definitions
define:
2.5 Structure Definitions
struct:
define-struct:
define-struct/  exec:
2.6 Names for Types
define-type
2.7 Generating Predicates Automatically
make-predicate
define-predicate
2.8 Type Annotation and Instantiation
:
provide:
ann
cast
inst
2.9 Require
require/  typed
require/  typed/  provide
2.10 Other Forms
with-handlers
lambda
λ
define
default-continuation-prompt-tag
#%module-begin
#%top-interaction
5.3.4

2 Special Form Reference

Typed Racket provides a variety of special forms above and beyond those in Racket. They are used for annotating variables with types, creating new types, and annotating expressions.

2.1 Binding Forms

loop, f, a, and v are names, t is a type. e is an expression and body is a block.

syntax

(let: ([v : t e] ...) . body)

(let: loop : t0 ([v : t e] ...) . body)
Local bindings, like let, each with associated types. In the second form, t0 is the type of the result of loop (and thus the result of the entire expression as well as the final expression in body). Type annotations are optional.

Examples:

> (: filter-even : (Listof Natural) (Listof Natural) -> (Listof Natural))
> (define (filter-even lst accum)
    (if (null? lst)
        accum
        (let: ([first : Natural (car lst)]
               [rest  : (Listof Natural) (cdr lst)])
              (if (even? first)
                  (filter-even rest (cons first accum))
                  (filter-even rest accum)))))
> (filter-even (list 1 2 3 4 5 6) null)

- : (Listof Nonnegative-Integer)

'(6 4 2)

Examples:

> (: filter-even-loop : (Listof Natural) -> (Listof Natural))
> (define (filter-even-loop lst)
    (let: loop : (Listof Natural)
          ([accum : (Listof Natural) null]
           [lst   : (Listof Natural) lst])
          (cond
            [(null? lst)       accum]
            [(even? (car lst)) (loop (cons (car lst) accum) (cdr lst))]
            [else              (loop accum (cdr lst))])))
> (filter-even-loop (list 1 2 3 4))

- : (Listof Nonnegative-Integer)

'(4 2)

syntax

(plet: (a ...) ([v : t e] ...) . body)

A polymorphic version of let:, abstracted over the type variables a. The type variables a are bound in both the types of the formal, and in any type expressions in the body. Does not support the looping form of let.

syntax

(letrec: ([v : t e] ...) . body)

syntax

(let*: ([v : t e] ...) . body)

syntax

(let-values: ([([v : t] ...) e] ...) . body)

syntax

(letrec-values: ([([v : t] ...) e] ...) . body)

syntax

(let*-values: ([([v : t] ...) e] ...) . body)

Type-annotated versions of letrec, let*, let-values, letrec-values, and let*-values. As with let:, type annotations are optional.

syntax

(let/cc: v : t . body)

syntax

(let/ec: v : t . body)

Type-annotated versions of let/cc and let/ec.

2.2 Anonymous Functions

syntax

(lambda: formals . body)

 
formals = ([v : t] ...)
  | ([v : t] ...    v : t *)
  | ([v : t] ...    v : t ...)
A function of the formal arguments v, where each formal argument has the associated type. If a rest argument is present, then it has type (Listof t).

syntax

(λ: formals . body)

An alias for the same form using lambda:.

syntax

(plambda: (a ...) formals . body)

A polymorphic function, abstracted over the type variables a. The type variables a are bound in both the types of the formal, and in any type expressions in the body.

syntax

(case-lambda: [formals body] ...)

A function of multiple arities. Note that each formals must have a different arity.

Example:

> (define add-map
    (case-lambda:
     [([lst : (Listof Integer)])
      (map add1 lst)]
     [([lst1 : (Listof Integer)]
       [lst2 : (Listof Integer)])
      (map + lst1 lst2)]))
For the type declaration of add-map look at case-lambda.

syntax

(pcase-lambda: (a ...) [formals body] ...)

A polymorphic function of multiple arities.

syntax

(opt-lambda: formals . body)

 
formals = ([v : t] ... [v : t default] ...)
  | ([v : t] ... [v : t default] ...    v : t *)
  | ([v : t] ... [v : t default] ...    v : t ...)
A function with optional arguments.

syntax

(popt-lambda: (a ...) formals . body)

A polymorphic function with optional arguments.

2.3 Loops

syntax

(for: type-ann-maybe (for:-clause ...)
  expr ...+)
 
type-ann-maybe = 
  | : u
     
for:-clause = [id : t seq-expr]
  | [id seq-expr]
  | #:when guard
Like for, but each id having the associated type t. Since the return type is always Void, annotating the return type of a for form is optional. Unlike for, multi-valued seq-exprs are not supported. Type annotations in clauses are optional for all for: variants.

syntax

(for/list: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/hash: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/hasheq: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/hasheqv: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/vector: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/flvector: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/and: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/or:   type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/first: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/last: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/sum: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for/product: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/list: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/hash: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/hasheq: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/hasheqv: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/vector: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/flvector: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/and: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/or:   type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/first: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/last: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/sum: type-ann-maybe (for:-clause ...) expr ...+)

syntax

(for*/product: type-ann-maybe (for:-clause ...) expr ...+)

These behave like their non-annotated counterparts, with the exception that #:when clauses can only appear as the last for:-clause. The return value of the entire form must be of type u. For example, a for/list: form would be annotated with a Listof type. All annotations are optional.

syntax

(for/lists: type-ann-maybe ([id : t] ...)
  (for:-clause ...)
  expr ...+)

syntax

(for/fold:  type-ann-maybe ([id : t init-expr] ...)
  (for:-clause ...)
  expr ...+)
These behave like their non-annotated counterparts. Unlike the above, #:when clauses can be used freely with these.

syntax

(for*: void-ann-maybe (for-clause ...)
  expr ...+)

syntax

(for*/lists: type-ann-maybe ([id : t] ...)
  (for:-clause ...)
  expr ...+)

syntax

(for*/fold:  type-ann-maybe ([id : t init-expr] ...)
  (for:-clause ...)
  expr ...+)
These behave like their non-annotated counterparts.

syntax

for

syntax

for*

These are identical to for and for*, but provide additional annotations to help the typechecker.

syntax

(do: : u ([id : t init-expr step-expr-maybe] ...)
         (stop?-expr finish-expr ...)
  expr ...+)
 
step-expr-maybe = 
  | step-expr
Like do, but each id having the associated type t, and the final body expr having the type u. Type annotations are optional.

2.4 Definitions

syntax

(define: v : t e)

(define: (f . formals) : t . body)
(define: (a ...) v : t e)
(define: (a ...) (f . formals) : t . body)
These forms define variables, with annotated types. The first form defines v with type t and value e. The third form does the same, but allows the specification of type variables. The second and fourth forms defines a function f with appropriate types. In most cases, use of : is preferred to use of define:.

Examples:

> (define: foo : Integer 10)
> (define: (add [first : Integer]
                [rest  : Integer]) : Integer
    (+ first rest))
> (define: (A) mt-seq : (Sequenceof A) empty-sequence)
> (define: (A) (poly-app [func : (A A -> A)]
                         [first : A]
                         [rest  : A]) : A
    (func first rest))

2.5 Structure Definitions

syntax

(struct: maybe-type-vars name-spec ([f : t] ...) options ...)

 
maybe-type-vars = 
  | (v ...)
     
name-spec = name
  | name parent
     
options = #:transparent
  | #:mutable
Defines a structure with the name name, where the fields f have types t, similar to the behavior of struct. When parent is present, the structure is a substructure of parent. When maybe-type-vars is present, the structure is polymorphic in the type variables v. If parent is also a polymorphic struct, then there must be at least as many type variables as in the parent type, and the parent type is instantiated with a prefix of the type variables matching the amount it needs.

Options provided have the same meaning as for the struct form.

syntax

(define-struct: maybe-type-vars name-spec ([f : t] ...) options ...)

 
maybe-type-vars = 
  | (v ...)
     
name-spec = name
  | (name parent)
     
options = #:transparent
  | #:mutable
Legacy version of struct:, corresponding to define-struct.

syntax

(define-struct/exec: name-spec ([f : t] ...) [e : proc-t])

 
name-spec = name
  | (name parent)
Like define-struct:, but defines a procedural structure. The procdure e is used as the value for prop:procedure, and must have type proc-t.

2.6 Names for Types

syntax

(define-type name t)

(define-type (name v ...) t)
The first form defines name as type, with the same meaning as t. The second form is equivalent to (define-type name (All (v ...) t)). Type names may refer to other types defined in the same module, but cycles among them are prohibited.

Examples:

> (define-type IntStr (U Integer String))
> (define-type (ListofPairs A) (Listof (Pair A A)))

2.7 Generating Predicates Automatically

syntax

(make-predicate t)

Evaluates to a predicate for the type t, with the type (Any -> Boolean : t). t may not contain function types, or types that may refer to mutable data such as (Vectorof Integer).

syntax

(define-predicate name t)

Equivalent to (define name (make-predicate t)).

2.8 Type Annotation and Instantiation

syntax

(: v t)

This declares that v has type t. The definition of v must appear after this declaration. This can be used anywhere a definition form may be used.

Examples:

> (: var1 Integer)
> (: var2 String)

syntax

(provide: [v t] ...)

This declares that the vs have the types t, and also provides all of the vs.

syntax

#{v : t}

This declares that the variable v has type t. This is legal only for binding occurrences of v.

syntax

(ann e t)

Ensure that e has type t, or some subtype. The entire expression has type t. This is legal only in expression contexts.

syntax

#{e :: t}

A reader abbreviation for (ann e t).

syntax

(cast e t)

The entire expression has the type t, while e may have any type. The value of the entire expression is the value returned by e, protected by a contract ensuring that it has type t. This is legal only in expression contexts.

Examples:

> (cast 3 Integer)

- : Integer

3

> (cast 3 String)

3: broke its contract

 promised: String

 produced: 3

 in: String

 contract from: cast

 blaming: cast

 at: eval:18.0

> (cast (lambda: ([x : Any]) x) (String -> String))

- : (String -> String)

#<procedure:val>

syntax

(inst e t ...)

Instantiate the type of e with types t .... e must have a polymorphic type with the appropriate number of type variables. This is legal only in expression contexts.

Examples:

> (foldl (inst cons Integer Integer) null (list 1 2 3 4))

- : (Listof Integer)

'(4 3 2 1)

> (: fold-list : (All (A) (Listof A) -> (Listof A)))
> (define (fold-list lst)
    (foldl (inst cons A A) null lst))
> (fold-list (list "1" "2" "3" "4"))

- : (Listof String)

'("4" "3" "2" "1")

syntax

#{e @ t ...}

A reader abbreviation for (inst e t ...).

2.9 Require

Here, m is a module spec, pred is an identifier naming a predicate, and r is an optionally-renamed identifier.

syntax

(require/typed m rt-clause ...)

 
rt-clause = [r t]
  | 
[#:struct name ([f : t] ...)
     struct-option ...]
  | 
[#:struct (name parent) ([f : t] ...)
     struct-option ...]
  | [#:opaque t pred]
     
struct-option = #:constructor-name constructor-id
  | #:extra-constructor-name constructor-id
This form requires identifiers from the module m, giving them the specified types.

The first case requires r, giving it type t.

The second and third cases require the struct with name name with fields f ..., where each field has type t. The third case allows a parent structure type to be specified. The parent type must already be a structure type known to Typed Racket, either built-in or via require/typed. The structure predicate has the appropriate Typed Racket filter type so that it may be used as a predicate in if expressions in Typed Racket.

Examples:

> (module UNTYPED racket/base
    (define n 100)
  
    (struct IntTree
      (elem left right))
  
    (provide n (struct-out IntTree)))
> (module TYPED typed/racket
    (require/typed 'UNTYPED
                   [n Natural]
                   [#:struct IntTree
                     ([elem  : Integer]
                      [left  : IntTree]
                      [right : IntTree])]))

The fourth case defines a new type t. pred, imported from module m, is a predicate for this type. The type is defined as precisely those values to which pred produces #t. pred must have type (Any -> Boolean). Opaque types must be required lexically before they are used.

In all cases, the identifiers are protected with contracts which enforce the specified types. If this contract fails, the module m is blamed.

Some types, notably the types of predicates such as number?, cannot be converted to contracts and raise a static error when used in a require/typed form. Here is an example of using case-> in require/typed.

(require/typed racket/base
               [file-or-directory-modify-seconds
                (case->
                  [String -> Exact-Nonnegative-Integer]
                  [String (Option Exact-Nonnegative-Integer)
                          ->
                          (U Exact-Nonnegative-Integer Void)]
                  [String (Option Exact-Nonnegative-Integer) (-> Any)
                          ->
                          Any])])

file-or-directory-modify-seconds has some arguments which are optional, so we need to use case->.

syntax

(require/typed/provide m rt-clause ...)

Similar to require/typed, but also provides the imported identifiers.

2.10 Other Forms

syntax

with-handlers

syntax

lambda

syntax

λ

syntax

define

Identical to with-handlers, lambda, λ, and define, respectively, but provide additional annotations to assist the typechecker. The define:, lambda:, and λ: forms are useful replacements which support type annotation.

Note that unlike define, define does not bind functions with keyword arguments to static information about those functions.

Identical to default-continuation-prompt-tag, but additionally protects the resulting prompt tag with a contract that wraps higher-order values, such as functions, that are communicated with that prompt tag. If the wrapped value is used in untyped code, a contract error will be raised.

Examples:

> (module typed typed/racket
    (provide do-abort)
    (: do-abort (-> Void))
    (define (do-abort)
      (abort-current-continuation
       ; typed, and thus contracted, prompt tag
       (default-continuation-prompt-tag)
       (λ: ([x : Integer]) (+ 1 x)))))
> (module untyped racket
    (require 'typed)
    (call-with-continuation-prompt
      (λ () (do-abort))
      (default-continuation-prompt-tag)
      ; the function cannot be passed an argument
      (λ (f) (f 3))))
> (require 'untyped)

default-continuation-prompt-tag: broke its contract

 Attempted to use a higher-order value passed as `Any` in

untyped code: #<procedure>

 in: ...

     the range of

      (-> (prompt-tag/c Any #:call/cc Any))

 contract from: untyped

 blaming: untyped

syntax

(#%module-begin form ...)

Legal only in a module begin context. The #%module-begin form of typed/racket checks all the forms in the module, using the Typed Racket type checking rules. All provide forms are rewritten to insert contracts where appropriate. Otherwise, the #%module-begin form of typed/racket behaves like #%module-begin from racket.

syntax

(#%top-interaction . form)

Performs type checking of forms entered at the read-eval-print loop. The #%top-interaction form also prints the type of form after type checking.