Conditional expressions
Typed Racket provides a number of special syntactic forms for testing conditions. These are similar to the mechanisms found in Racket and are described in detail in Chapter 4 of the text.
Cond expressions
In Typed Racket, we usually use the cond
construct to specify conditional code.
Its basic form is
(cond [pred action] [pred action] ...)
where pred
is a predicate expression, that has Boolean
type,
and action
is an expression to evaluate when the predicate holds.
The last predicate can be the reserved word else
.
The evaluation rules for conditionals are
|
⇒ |
|
|
⇒ |
|
|
⇒ |
|
For example, we could define the absolute-value function as
(: absolute-value : Real -> Real)
(define (absolute-value x)
(cond
[(< x 0) (- x)]
[else x]))
There is a subtle type-checking issue when using conditionals. The actions
of a conditional must all have the same type. The problem arises when a cond
expression is incomplete. In that situation, the compiler adds an extra
else
clause to complete the cond
, but the type of the action is Void
(more about Void
later), which will likely conflict with the types of the
actions that you wrote. For example, consider the following two Typed Racket
function definitions:
(: f : Boolean -> String)
(define (f b)
(cond
[(eq? b #f) "false"]
[(eq? b #t) "true"]))
(: g : Integer -> String)
(define (g i)
(cond
[(= i 0) "false"]
[(= i 1) "true"]))
The first one (f
) has a complete cond
and will pass the type checker, but the second (g
)
has an incomplete cond
(e.g., what if the argument is 2
?) and will produce the
type error.
Type Checker: type mismatch expected: String given: Void in: (cond ((= i 0) "false") ((= i 1) "true"))
We can fix this problem by either converting the last predicate to else
(assuming that
that makes sense for the function) or by adding our own else
clause.
(: g : Integer -> String)
(define (g i)
(cond
[(= i 0) "false"]
[(= i 1) "true"]
[else "error"]))
Unfortunately, there will be situations where the compiler is unable to determine
that a cond
expression is, in fact, complete as in the following example:
(: h : Boolean -> String)
(define (h b)
(cond
[(eq? (not b) #t) "false"]
[(eq? b #t) "true"]))
There is a tradition in Lisp-family languages, like Racket, that any value that is
not
and Typed Racket will not complain. This expression happens to evaluate to |
If expressions
A more compact way to write a complete cond
of two clauses is to use the if
form,
which has the syntax
(if pred then-action else-action)
and is equivalent to the cond
expression
(cond
[pred then-action]
[else else-action])
Logical connectives
Typed Racket also provides the standard two logical connectives — and
and or
— for
writing conditional expressions.
These have the standard logical definition as defined by the following table
a | b | (and a b ) |
(or a b ) |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
There are two differences, however, from the standard binary connectives found in logic.
First, like the arithmetic operators, and
and or
can be used on an arbitrary number
of arguments. Second, they are non-strict in their arguments. In other words, they
will short circuit evaluation when possible. The following evaluation rules define
their semantics:
|
⇒ |
|
|
⇒ |
|
|
⇒ |
|
|
⇒ |
|
|
⇒ |
|
|
⇒ |
|
Reporting errors
Typed Racket has a function error
that we can use to report errors. This function
has the following (simplified) type
(: error : String -> Nothing)
(the return type of Nothing
signifies that it does not return). The convention is
to pass a string that describes the error, often beginning with the name of the enclosing function followed by a colon.
For example
(: fact : Integer -> Integer)
;; compute the factorial of n; signal an error on negative inputs
(define (fact n)
(cond
[(<= n 0) (error "fact: expected positive argument")]
[(= n 0) 1]
[else (* n (fact (- n 1)))]))
If we pass fact
a negative argument, we will get an error message as in the following
REPL interaction:
> (fact -2)
fact: expected positive argument
We can test the error handling of functions using check-error
, which takes the
expression to evaluate and the expected error message as arguments. For example
(check-error (fact -2) "fact: expected positive argument")
In stock Typed Racket, the |