Contracts#

Warning

Contracts are largely a work in progress. Crochet is currently missing ways of reusing contracts and applying contracts to more complex concepts.

While example-based testing lets us describe specific scenarios of our programs and what we expect to happen in them, contracts allow us to specify things that always have to be true.

Currently Crochet allows contracts to be specified for commands only.

Pre- and post-conditions#

Crochet’s contracts for commands come in the form of pre- and post-conditions. A pre-condition describes things that need to be true about the program before the command is executed, whereas a post-condition describes things that need to be true after the command is executed.

For example, consider the case where we’re building an inventory, and we have a command to take something out of the inventory. Here, a pre-condition could be that the item exists in the inventory, and the post-condition could be that the item does not exist in the inventory anymore.

If we were to express this in Crochet, it could take this form:

command inventory remove: Item
requires
  item-exists :: self.items contains: Item
ensures
  item-removed :: not (self.items contains: Item)
do
  // The code for the command goes here
end

In the requires section of the command we specify all of the properties that have to be true before it executes. Here we have just one: item-exists, which says that whatever we’re trying to remove must exist in the inventory to begin with.

The ensures section describe the same for post-conditions. Both pre-conditions and post-conditions may contain several of these properties, each of them separated by a comma.

For example, the integer to: _ by: _ command gives us a sequence where we start with a number, and keep adding some small increment to it until we reach a given limit. The contract for this command specifies two pre-conditions:

command (Start is integer) to: (End is integer) by: (Step is integer)
requires
  progress :: Step > 0,
  ordered :: Start <= End
do
  // Implementation intentionally omitted
end

Here, the progress property ensures that we can get a sequence at all. If someone provided a step of 0 by mistake, the command would never be able to finish—adding zero to any number just gives us that number!

We also care about the sequence being ordered—the ending number must be greater than the starting number, otherwise we’ll never be able to reach it.