So far we have talked about how intended functions describe
the semantics of a prime — what the code does.
However, your code may not be able to guarantee all the
conditions necessary for successful operation. This means
that you may specify *preconditions* in
order shift the burden of certain conditions onto the caller.

Let's take for example a function `sqrt(x)`

that computes the square root. Here's our first attempt:

# [ y := square root of x ] y = math.sqrt(x)

However, what if the caller passes a negative argument?
Worse yet, what if the caller passes a string argument?
We're expecting `x`

to be a number!

The way to handle this is to specify one or more
preconditions. A precondition is a statement that must
be true for the code to work properly. Here's our
revised intended function for `sqrt(x)`

.

# [ x is a nonnegative number -> # y := square root of x ]

Another name for this convention is *partial
specification*. We define what our function
does if `x`

is a nonnegative number, but we
don't define what it does for any other cases. The
precondition precedes the rest of the intended function,
followed by “`->`

”.

Another way to read this intended function is as a contract:

If the caller insures that all preconditions are true, the prime guarantees to make the specified changes in state items.

Recall Stan Kelly-Bootle's definition of an interface in
Section 2, “The contract-based approach to program
construction”? With our improved intended
function, we can apportion the blame for malfunctions
quite clearly. If the caller supplied a nonnegative
number and didn't get the right result, it's the fault of
the `sqrt`

function. If the caller
*didn't* supply a nonnegative
number, it's the *caller's* fault if
the result is wrong.

Here's an example of multiple preconditions: an intended
function for the `atan2(y,x)`

function in
the standard Python `math`

module.

# [ (y is a number) and (x is a number) -> # return the angle that a vector from the origin to # (x,y) makes relative to the x-axis, in radians, in # the range [-pi, pi] ]

What about functions and methods with optional arguments?
Here's an example intended function to describe the `s.rjust(n[, fill])`

method of the Python `str`

type.

# [ (s is a string) and (n is a positive int) and # (fill is a string) -> # return s, left-padded to length n with fill (defaulting # to spaces) ]

There are other ways to write this intended function.
For example, you could write the function signature as
“`s.rjust(n, fill=' ')`

”, in
which case the intended function wouldn't even have to
mention the default value process because it is built
into the Python semantics for function calls: if you
leave out the second argument to `.rjust()`

,
it's the same as if you passed a space for that argument.