Trammel

a contracts-programming library for Clojure

Contracts Programming

the relationship between a class and its clients as a formal agreement, expressing each party's rights and obligations

read more

As you saw in the description of defconstrainedfn Trammel allows you to create functions with a localized and dependent contract. However, there may be circumstances where the separation of contract and constrained function is preferred. Take for example, a simple slope function:

(defn sqr [n] (* n n))

Defining a separate contract for this function is a simple matter:

(def sqr-contract
  (contract sqr-constraints
    "Defines the constraints for squaring"
    [n] [number? (not= 0 n) => pos? number?]))

We can then define a constrained version of sqr using Trammel’s with-constraints macro:

(def constrained-sqr
  (with-constraints
    sqr
    sqr-contract))

(constrained-sqr 5)
;=> 25

(constrained-sqr -5)
;=> 25

(constrained-sqr 0)
; java.lang.AssertionError: Assert failed:
(not= 0 num)

(constrained-sqr :a)
; java.lang.AssertionError: Assert failed:
(number? num)

However, this constraint definition for sqr, while accurate, is very broad. In fact, the software team developing software for the 8-bit Atari console would not be able to use constrained-sqr as it is far too liberal. Therefore, they can define their own contract that further constrains constrained-sqr:

(def sqr-8bit-contract
  (contract atari-constraints
    "Defines the constraints for Atari 2600 sqr"
    [_] [number? => (< % 256)]))

(def sqr-8bit
  (with-constraints
    constrained-sqr
    sqr-8bit-contract))

(sqr-8bit 5)
;=> 25

(sqr-8bit 0)
; java.lang.AssertionError:
; Assert failed: (not= 0 num)

And all appears to be in order – except:

(sqr-8bit 100)
; java.lang.AssertionError:
; Assert failed: (< % 256)

That is, calling the function sqr-8bit with 100 causes a post-condition failure! The reason for this is because the underlying sqr is the same old arbitrary-precision version when what we really want is a function that deals in only 8-bit values. There are two possible ways to do this:

  1. Create a version of sqr-8bit that does in fact deal in 8-bit values
  2. Tighten the constraint on constrained-sqr further by applying another contract
(def sqr-8bit-contract
  (contract atari-constraints
    "Defines the constraints for Atari 2600 sqr"
    [n] [(< n 16) integer? pos? => (< % 256)]))

(def sqr-8bit
  (with-constraints
    constrained-sqr
    sqr-8bit-contract))

(sqr-8bit 15)
;=> 225

(sqr-8bit -5)
; java.lang.AssertionError:
; Assert failed: (pos? n)

(sqr-8bit 15.9687194)
; java.lang.AssertionError:
; Assert failed: (integer? n)

(sqr-8bit 16)
; java.lang.AssertionError:
; Assert failed: (< n 16)

Using contract and with-constraints you were able to tighten the constraints on both the pre- and post-conditions of the sqr function. However, what if you wanted to relax the requirements? Stay tuned.

return to documentation

clojars → source code → tickets → wiki →