On this page:
2.1 Datatypes and Unions
2.2 Type Errors
5.3.6

2 Beginning Typed Racket

Recall the typed module from Quick Start:

#lang typed/racket
(struct: pt ([x : Real] [y : Real]))
 
(: distance (pt pt -> Real))
(define (distance p1 p2)
  (sqrt (+ (sqr (- (pt-x p2) (pt-x p1)))
           (sqr (- (pt-y p2) (pt-y p1))))))

Let us consider each element of this program in turn.

#lang typed/racket

This specifies that the module is written in the typed/racket language, which is a typed version of the racket language. Typed versions of other languages are provided as well; for example, the typed/racket/base language corresponds to racket/base.

(struct: pt ([x : Real] [y : Real]))

Many forms in Typed Racket have the same name as the untyped forms, with a : suffix.

This defines a new structure, name pt, with two fields, x and y. Both fields are specified to have the type Real, which corresponds to the real numbers. The struct: form corresponds to the struct form from racketwhen porting a program from racket to typed/racket, uses of struct should be changed to struct:.

(: distance (pt pt -> Real))

This declares that distance has the type (pt pt -> Real).

The type (pt pt -> Real) is a function type, that is, the type of a procedure. The input type, or domain, is two arguments of type pt, which refers to an instance of the pt structure. The -> both indicates that this is a function type and separates the domain from the range, or output type, in this case Real.

(define (distance p1 p2)
  (sqrt (+ (sqr (- (pt-x p2) (pt-x p1)))
           (sqr (- (pt-y p2) (pt-y p1))))))

This definition is unchanged from the untyped version of the code. The goal of Typed Racket is to allow almost all definitions to be typechecked without change. The typechecker verifies that the body of the function has the type Real, under the assumption that p1 and p2 have type pt, taking these types from the earlier type declaration. Since the body does have this type, the program is accepted.

2.1 Datatypes and Unions

Many data structures involve multiple variants. In Typed Racket, we represent these using union types, written (U t1 t2 ...).

#lang typed/racket
(define-type Tree (U leaf node))
(struct: leaf ([val : Number]))
(struct: node ([left : Tree] [right : Tree]))
 
(: tree-height (Tree -> Integer))
(define (tree-height t)
  (cond [(leaf? t) 1]
        [else (max (+ 1 (tree-height (node-left t)))
                   (+ 1 (tree-height (node-right t))))]))
 
(: tree-sum (Tree -> Number))
(define (tree-sum t)
  (cond [(leaf? t) (leaf-val t)]
        [else (+ (tree-sum (node-left t))
                 (tree-sum (node-right t)))]))

In this module, we have defined two new datatypes: leaf and node. We’ve also defined the type name Tree to be (U node leaf), which represents a binary tree of numbers. In essence, we are saying that the tree-height function accepts a Tree, which is either a node or a leaf, and produces a number.

In order to calculate interesting facts about trees, we have to take them apart and get at their contents. But since accessors such as node-left require a node as input, not a Tree, we have to determine which kind of input we were passed.

For this purpose, we use the predicates that come with each defined structure. For example, the leaf? predicate distinguishes leafs from all other Typed Racket values. Therefore, in the first branch of the cond clause in tree-sum, we know that t is a leaf, and therefore we can get its value with the leaf-val function.

In the else clauses of both functions, we know that t is not a leaf, and since the type of t was Tree by process of elimination we can determine that t must be a node. Therefore, we can use accessors such as node-left and node-right with t as input.

2.2 Type Errors

When Typed Racket detects a type error in the module, it raises an error before running the program.

Example:

> (add1 "not a number")

eval:2:0: Type Checker: No function domains matched in

function application:

Types: Zero -> One

       One -> Positive-Byte

       Byte -> Positive-Index

       Index -> Positive-Fixnum

       Negative-Fixnum -> Nonpositive-Fixnum

       Nonpositive-Fixnum -> Fixnum

       Nonnegative-Integer -> Positive-Integer

       Negative-Integer -> Nonpositive-Integer

       Integer -> Integer

       Nonnegative-Exact-Rational -> Positive-Exact-Rational

       Exact-Rational -> Exact-Rational

       Nonnegative-Flonum -> Positive-Flonum

       Flonum -> Flonum

       Nonnegative-Single-Flonum -> Positive-Single-Flonum

       Single-Flonum -> Single-Flonum

       Nonnegative-Inexact-Real -> Positive-Inexact-Real

       Inexact-Real -> Inexact-Real

       Nonnegative-Real -> Positive-Real

       Real -> Real

       Float-Complex -> Float-Complex

       Single-Flonum-Complex -> Single-Flonum-Complex

       Inexact-Complex -> Inexact-Complex

       Number -> Number

Arguments: String

Expected result: AnyValues

  in: (#%app add1 (quote "not a number"))