the relationship between a class and its clients as a formal agreement, expressing each party's rights and obligations
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:
sqr-8bit
that does in fact deal in 8-bit valuesconstrained-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.