Fork me on GitHub
Run!
CoffeeScript
JavaScript

      
Errors

contracts.coffee

Contracts.coffee is a dialect of CoffeeScript with built-in support for contracts. It is inspired by the contract system found in Racket.

Contracts let you clearly—even beautifully—express how your code behaves, and free you from writing tons of boilerplate, defensive code.

You can think of contracts as assert on steroids.

Basics

Here’s a simple example of a contract on a function:

id :: (Num) -> Num
id = (x) -> x

This says that the id function should always be called with a number and will always return a number. It looks a lot like types (in fact the syntax looks a lot like Haskell) but unlike types, contracts are enforced at runtime in pure JavaScript.

If we try to use id incorrectly:

id "foo"

The program throws an error, which displays lots of nice information telling us what we did wrong:

Error: Contract violation: expected <Num>,
actual: "foo"
Value guarded in: id_module:42
  -- blame is on: client_code:104
Parent contracts:
(Num) -> Num

You can also put contracts on objects.

person ::
    name: Str
    age: Num
person =
    name: "Bertrand Meyer"
    age: 42

And arrays.

loc :: [...Num]
loc = [99332, 23452, 123, 2, 5000]

And in various combinations.

average :: ({name: Str, age: Num}, [...Num]) -> Str
average = (person, loc) ->
    sum = loc.reduce (s1, s2) -> s1 + s2
    "#{person.name} wrote on average 
     #{sum / loc.length} lines of code."

Under the covers, contracts are really just normal functions that return true or false, so it’s really easy to roll your own by using the ! operator to define new contracts.

Even = (x) -> x % 2 is 0
Odd = (x) -> x % 2 isnt 0

addEvens :: (!Even) -> !Odd
addEvens = (x) -> x + 1

In fact, since contracts are checked at runtime, they can enforce properties that static type systems can only dream of.

Prime = (x) -> # ...

f :: (!Prime) -> !Prime
f = (x) -> x

Eat your heart out, Haskell :)

Quick Start

Here’s what you need to actually start working with contracts.coffee.

First, if you don’t already have them, install Node.js and npm. Then install contracts.coffee using npm:

npm install -g contracts.coffee

Now, compile some coffee with contracts!

coffee -c --contracts MyContractedScript.coffee

Note the -c and --contracts flags. The -c flag says to compile to JavaScript and --contracts enables contracts in the compiled JavaScript. If you don’t want contracts enabled (say in production) simply don’t include the --contracts flag.

If you are planning to run your code in the browser you will need to include the contracts.js library (which can be found here). So the header of your HTML file will look something like:

...
<script src="lib/contracts.js"
    type="application/javascript"></script>
<script src="MyContractedScript.js"
    type="application/javascript"></script>
...

Contracts.coffee also has experimental support for require.js. Just load the “contracts” module first in main.js. So your main.js will look something like:

require(["contracts", "MyModule"], function(c, myMod) {
    // ...
});

If you are planning to run your code on node.js then you simply need to install contracts.js via npm:

npm install contracts.js

Note that if you have an existing install of CoffeeScript, installing contracts.coffee will replace it. If you don’t want to give up the old CoffeeScript compiler you can grab the source from github and just run contracts.coffee from its own directory:

bin/coffee -c --contracts MyContractedScript.coffee

And finally, note that contracts.coffee requires some pretty new features of JavaScript to get its job done (in particular Proxies) so it currently only works on Firefox 4+, Node.js 0.8.0+, and recent versions of Chrome (though at the moment you’ll need to enable the experimental JavaScript flag in about:flags).

When using node you will need to supply two command line flags to enable Proxies (--harmony_proxies) and WeakMaps (--harmony-collections). If you use the coffee or cake scripts these flags will be enabled automatically for you, otherwise the full process looks like:

coffee -c --contracts script.coffee
node --harmony_proxies --harmony-collections script.js

Note that since leaving off the --contracts flag will generate JavaScript code with absolutely no trace of contracts (the code is exactly what vanilla CoffeeScript would generate), you can easily set up a production build with contracts disabled that can run in any browser or JavaScript environment and a development/testing build with contracts enabled that you run in Firefox to help track down bugs.

Resources

How to Use

In order to provide good error messages when things go wrong, contracts.coffee needs to know where contracted values are created and used in your code. It does this by enforcing a kind of module discipline and keeping track of which module a value was in when it was first wrapped up in a contract and which module uses the contracted value.

Since JavaScript doesn’t (yet) have modules, contracts.coffee must enforce a notion of modules on its own. The good news is that for the common cases you’ll never have to deal with this.

  • If you are running in node.js the appropriate module wiring is done automatically. You just need to use require and the exports object like normal.

  • If you are running in the browser and using require.js, then the wiring is also automatically handled for you. Just be sure to include “contracts” as your first loaded module. (Here is an example of this in action)

  • If you have some other situation then you will need to do the module wiring by hand. This is documented over here.

Simple Contracts

In addition to the Num contract that checks for numbers, we also have Str, Bool, Null, Undefined, Nat, Pos, Neg, Any (everything is ok), and None (nothing is ok).

Functions

Basic functions:

f :: (Num) -> Num
f = (x) -> x

Multiple arguments:

f :: (Num, Str, Bool) -> Num
f = (n, s, b) -> # ...

Optional arguments:

f :: (Num, Str, Bool?) -> Num
f = (n, s, b) -> # ...

All optional arguments must come at the end of the arguments list.

Higher order functions:

f :: ((Num) -> Bool, Num) -> Bool
f = (g, n) -> # ...

Functions that cannot be called with the new keyword:

f :: (Num) --> Num
f = (n) -> # ...
#...

g = f 42      # ok
g = new f 42  # error!

Functions that can only be called with the new keyword:

f :: (Num) ==> Num
f = (n) -> # ...
#...

g = f 42      # error!
g = new f 42  # ok

Dependent functions:

inc :: (Num) -> !(result, args) -> result > args[0]
inc = (n) -> n + 1

The variable args[0] is the first argument passed to the function (args[1] would be the second argument, args[2] the third, and so on). This allows us to compare the result of the function to its arguments. Note that the test is run after the function has completed so if any of the arguments were mutated during the function’s execution the test could give spurious results.

The this contract:

f :: (Str, @{name: Str}) -> Str
f = (s) -> #...

o = { name: "foo", f: f}
o.f()

Checks that this matches the given object contract.

Objects

Simple properties:

o ::
  a: Str
  b: Num
  f: (Num) -> Num
o =
  a: "foo"
  b: 42
  f: (x) -> x

Note: Putting an object contract on a primitive will always fail. This might seem obvious at first but since many primitives have methods, the following contract would seem reasonable:

f :: ({toString: (Any) -> Str}) -> Str
f = (s) -> s.toString()

f "a string"   # Contract violation
f {}           # ok

Even though “a string” has a toString method the contract will always signal a violation. This is because JavaScript proxies can wrap objects but not primitives.

In addition due to a limitation in the current JavaScript engines implementations of proxies, you cannot put a contract on the Date object. This is expected to be fixed in future implementations. See this issue to track progress on this.

Optional properties:

o ::
  a: Str
  b: Num?
  f: (Num) -> Num
o =
  a: "foo"
  f: (x) -> x

Nested objects:

o ::
  oo: { a: Str }
  b: Num
o =
  oo: { a: "foo" }
  b: 42

Recursive objects:

o ::
  a: Num
  b: Self
  c: (Num) -> Self
  inner: { y: Bool, z: Self }
o = #...

Self binds to the closest object contract. So in this example, Self in b and c points to o and Self in inner.z points to inner.

Objects with functions that have pre and post conditions:

o ::
  a: Num
  f: (Num) -> Num -|
      pre: (o) -> o.a > 10
      post: (o) -> o.a > 20
o =
  a: 12
  f: (x) -> @.a = @.a + x

The pre and post condition functions are called with the object that f is a member of. As their names imply, pre is called before the function f is invoked and post is called after.

Object invariants:

o ::
  a: Num
  f: (Num) -> Num -|
      pre: (o) -> o.a > 10
      post: (o) -> o.a > 20
  -| invariant: ->
    @.a > 0 and @.a < 100
o =
  a: 12
  f: (x) -> @.a = @.a + x

The invariant is checked at contract application and whenever there is a possibility of o mutating (on property sets and delete).

Arrays

Note: due to a proxy bug in all the current JavaScript engines, arrays are not currently being wrapped in a contract. This won’t cause code to fail, it just means that contracts will not be checked for arrays. See this github issue for more info.

Basic arrays:

a :: [Num, Str, [Bool, Num]]
a = [42, "foo", [true, 24]

This says the array must have three elements, the first being a Num, the second being a Str, and the third being another array.

Multiple elements:

a :: [...Num]
a = [42, 24, 432, 854, 21]

The ... operator says that the array will only contain Nums.

Mixing arrays

a :: [Bool, Str, ...Num]
a = [false, "foo", 432, 854, 21]

The ... operator can be mixed with single contracts. This says that the array’s first and second positions must pass the first two contracts and the remaining array positions must pass Num. The ... operator must be in the last position of the array contract.

Contract Operators

The or contract:

o :: { a: Num or Str }
o = { a: 42 }

Here, the a property must pass either the Num or Str contract. Note that since contracts like function and object have deferred checking, they cannot be used with the or contract. Or to be more precise only one function/object contract can be used with or. So you could have Num or (Num) -> Num but not ((Num) -> Num) or ((Str) -> Str). If you combine normal contracts and function/object contracts with or all the normal contracts will be checked first and then the function/object contract will be applied.

The and contract:

o :: { a: Num and Even }
o = { a: 42 }

The a property must pass both the Num and Even contracts. Just like or you cannot use multiple function/object contracts with and.

Check-Time of Contracts

The actual point in time when a contract is checked depends on the kind of contract.

  • Simple contracts (e.g. Num, Str, and contracts created via !) are checked immediately.
  • Function contracts are checked when the function is called.
  • Object contracts are immediately checked to make sure all their properties exist. The individual property contracts are checked each time the property is accessed.
  • Array contracts are checked in the same way as object contracts.

For example, this means that if a function f takes another function g as an argument, it will delay checking of g until g is actually invoked.

f :: ((Num) -> Num, (Str) -> Str) -> Num
f = (g, h) -> g 42

str = (x) -> "string"
num = (x) -> 42

f str, num  # fails when g is called inside f
f num, num  # since h is never called it never fails
            # even though it would violate its contract

A function returning an object will immediately check for the existence of properties but delay checking that they match their contract until accessed.

f :: (Num) -> {a: Str, b: Num}
f = (x) -> {a: "foo"}

# fails as soon as f returns since b is missing
f 42

g :: (Num) -> {a: Str, b: Num}
g = (x) -> {a: x, b: x}

o = g 42         # does not fail yet
console.log o.a  # now fails because o.a does not satisfy Str

Naming your own contracts

You can bind a contract to a variable just like normal expressions:

NumId = ?(Num) -> Num

f :: NumId
f = (x) -> x

The ? operator allows you to escape out of the normal expression language and into the contract language.

You can also escape from the contract language to the normal expression language:

takesEvens :: (!(x) -> x % 2 is 0) -> Num
takesEvens = (x) -> x

The result of the expression in the ! escape must be a function that returns a boolean. It is converted to a contract that checks its value against the function.

The standard Num and Str contracts you have seen are implemented as:

Num = ?!(x) -> typeof x is 'number'
Str = ?!(x) -> typeof x is 'string'

The syntax may look a little strange at first, but it can be read as “assign to Num the contract generated from the function (x) -> ...

It could also be written:

Num = (x) -> typeof x is 'number'
Str = (x) -> typeof x is 'string'

f = (!Num) -> !Str

Duck-Typing Invariants

A full write-up on this topic is covered here but to whet your appetite: you can “duck-type” object invariants. Code can now say, “give me whatever object you want so long as it has these properties and satisfies these invariants”.

Consider a binary search tree:

# A binary search tree is a binary tree 
# where each node is  greater than the 
# left child but less than the right child
BinarySearchTree = ?(Null or {
  node: Num
  left: Self or Null
  right: Self or Null
  -| invariant: ->
    (@.node > @.left.node) and (@.node < @.right.node)
})

And a red-black tree:

# A red-black tree is a binary search tree 
# that keeps its balance
RedBlackTree = ?(Null or {
  node: Num
  color: Str
  left: Self or Null
  right: Self or Null
  -| invariant: ->
    (@.color is "red" or @.color is "black") and
    (if @.color is "red"
      (@.left.color is "black" and 
       @.right.color is "black") 
    else 
      true
    ) and
    (@.node >= @.left.node) and 
    (@.node >= @.right.node) and
})

The red-black tree is exactly the same as a binary search tree with some additional invariants. This means we have a kind of subtyping going on here: code that expects a binary search tree will also work with a red-black tree but not vica versa.

takesBST :: (BinarySearchTree) -> Any
takesBST = (bst) -> ...

takesRedBlack :: (RedBlackTree) -> Any
takesRedBlack = (rbTree) -> ...

bst = makeBinarySearchTree()  
rb = makeRedBlackTree()

takesBST bst # works fine
takesBST rb  # works fine

takesRedBlack rb  # works fine
takesRedBlack bst # might fail if the full 
                  # red-black invariants don't hold!

In duck-typing, functions work when given any object that has the properties the function needs (though the object might have other properties too). Contracts allow us to extend that to object invariants: functions work when given any object that has the required properties and satisfies the required invariants (though the object might satisfy other invariants too).

Change Log

  • 0.3.2 (September 5, 2012)

    • disabling contracts for arrays (see issue 54)
    • various bug fixes
  • 0.3.1 (July 15th, 2012)

    • support for stable node.js (v0.8.0+)
    • some bug fixes
  • 0.3.0 (March 15th, 2012)

    • change to dependent function contracts
    • various contracts.coffee bug fixes: #27, #41, #33, #31,
    • various contracts.js bug fixes: #9, #8, #7, #6, #5, #4
    • experimental support for require.js (see here for example use)
    • based off CoffeeScript 1.3.1
  • 0.2.0 (January 4th, 2012)

    • removed .use(), now using Contracts.exports and Contracts.use
    • various bug fixes
    • based off CoffeeScript 1.2.0
  • 0.1.0 (August 29th, 2011)

    • initial release
    • based off CoffeeScript 1.1.2