Function contracts
In this course (and in the
text)
each function definition is preceded by a contract and a header comment.
Most of the contracts that we will use in this class can be captured by types
and checked by the computer. The informal syntax used by the text for contracts
is a close match to the formal syntax of types used by Typed Racket; and can
be copied with only small changes.
For example, the
text
gives the following contract, header, and purpose statement for the profit
function:
;; profit : number -> number
;; to compute the profit as the difference between revenue and costs
;; at some given ticket-price
(define (profit ticket-price) ...)
The use of …
to specify an incomplete function body is not supported in Typed Racket,
so we give the function’s definition when we translate:
(: profit (-> Number Number))
;; to compute the profit as the difference between revenue and costs
;; at some given ticket-price
(define (profit ticket-price)
(- (revenue ticket-price)
(cost ticket-price)))
Notice that we had to change number
to Number
; type names in Typed
Racket begin with capital letters.
Variable definitions
Variable definitions should also be proceeded by a type ascription. The syntax is essentially the same as for functions. For example:
(: e Real)
(define e (exp 1))
(The function exp
is built in, and gives the value of e raised to
the given power.)
Testing
DrRacket comes with a bundle of tools for testing. There are many of
these; the ones that are immediately useful to you are check-expect
and check-within
, for testing exact and inexact results,
respectively. (Other useful checks
are check-range
and
check-error
, among others.)
To use the check
tools, at the top of your definitions window
(i.e., the beginning of the program) write the following require
directive:
(require typed/test-engine/racket-tests)
And at the end of your program, you need a command to run the tests:
(test)
The check-expect
form tests to see if an expression evaluates
to an expected result.
(check-expect expr expected-expr)
For example, we can add tests for the sq
function that we defined above:
(: sq (-> Number Number))
;; return the square of a number
;;
(define (sq x)
(* x x))
(check-expect (sq 5) 25)
(check-expect (sq -1) 1)
When computing with real numbers, the results are often inexact. In that
situation we use the check-within
form to test if an expression evaluates to
within a given delta of an expected result.
(check-within expr-1 expr-2 delta-expr)
For example
(check-within (sq (sqrt 2)) 2 0.001)
Note that the test
function runs all of the tests defined up to its invocation.
Therefore, you should only invoke it once after all of the tests have been
defined.
Querying types
It is possible to query the type of an identifier using DrRacket’s REPL using
the :print-type
function:
> (:print-type sq)
(-> Number Number)
Notice that Typed Racket prints function types using the prefix syntax. Thus, even if you prefer the more natural infix arrow syntax when you write your own code, you will need to be comfortable with the prefix syntax as well.