Skip to content
Contracts programming with Clojure
Clojure Ruby
Find file
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Contracts programming for Clojure.


Function Contracts

    (require '[trammel.provide :as provide])

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

    (sqr 10)
    ;=> 100
    (sqr 0)
    ;=> 0

      [sqr "given a number not equal to zero, sqr ensures that it returns a positive number"
        [x] [number? (not= 0 x) => number? pos?]])

    (sqr 10)
    ;=> 100
    (sqr 0)
    ; Pre-condition failure: given a number not equal to zero, sqr
    ;   ensures that it returns a positive number
    ; Assert failed: (not= 0 x)

Record Invariants

    (use '[trammel.core :only (defconstrainedrecord)])

    (defconstrainedrecord Foo [a 1 b 2]
      "Foo record fields are expected to hold only numbers."
      [(every? number? [a b])]
      (toString [this] (str "record Foo has " a " and " b)))

    ;; default ctor with default values
    ;=> #:user.Foo{:a 1, :b 2}

    ;; kwarg ctor
    (->Foo :a 42)
    ;=> #:user.Foo{:a 42, :b 2}

    ;; use like any other map/record
    (assoc (->Foo) :a 88 :c "foo")
    ;=> #:user.Foo{:a 88, :b 2, :c "foo"}

    ;; invariants on records checked at runtime    
    (assoc (->Foo) :a "foo")
    ; Assert failed: (every? number? [a b])

Type Invariants

    (use '[trammel.core :only (defconstrainedtype)])

    (defconstrainedtype Foo [a b]
      "Foo type fields are expected to hold only numbers."
      [(every? number? [a b])])

    (->Foo 1 2)
    #<Foo user.Foo@73683>

    ;; invariants on types checked at constructions time
    (->Foo 1 :b)
    ; Assert failed: (every? number? [a b])



Modify your Leiningen dependencies to include Trammel:

    :dependencies [[trammel "0.7.0-SNAPSHOT"] ...]


Add the following to your pom.xml file:



Trammel is in its infancy but I think that I have a nice springboard for experimentation and expansion, including:

  • Contracts for higher-order functions
  • Better error messages
  • Distinct pre and post exceptions
  • Study the heck out of everything Bertrand Meyer and Walter Bright ever wrote (in progress)
  • defconstraint -- with ability to relax requires and tighten ensures
  • Study the heck out of Racket Scheme (in progress)
  • Modify macros to also allow regular Clojure constraint maps
  • Reference contracts
  • Make the anything constraint cheap (elimination)
  • Allow other stand-alones: true/false, numbers, characters, regexes
  • Make provide-contracts more amenable to REPL use
  • Generate a Foo? function (in progress)
  • Marrying test.generative with Trammel

If you have any ideas or interesting references then I would be happy to discuss at me -the-at-sign- fogus -the-single-period- me.



Add the following to your .emacs file for better Trammel formatting:

    (eval-after-load 'clojure-mode
         (contract 'defun)
         (defconstrainedfn 'defun)
         (defcontract 'defun)
         (provide 'defun)))

Example REPL Session

Type the following into a REPL session to see how Trammel might be used.

    (defconstrainedtype Bar 
      [a b] 
      [(every? pos? [a b])])

    (Bar? (->Bar 1 2))

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

      [sqr "the constraining of sqr" 
        [n] [number? (not= 0 n) => pos? number?]])

    (sqr 0)

    (positive-nums -1)

    (type (->Bar))

    (.a (->Bar  42 77))
    (.b (->Bar  42 77))
    (.a (->Bar -42 77))
    (.b (->Bar  42 -77))

    (defconstrainedfn sqrt
      [x] [(>= x 0) => (>= % 0)]
      (Math/sqrt x))

    (defn- bigger-than-zero? [n] (>= n 0))

    (defconstrainedfn sqrt
      [x] [bigger-than-zero? => bigger-than-zero?]
      (Math/sqrt x))

    (sqrt 10)
    (sqrt -19)

    (defconstrainedfn sqrt
      [x] [bigger-than-zero? => bigger-than-zero? (<= (Math/abs (- x (* % %))) 0.01)]
      (Math/sqrt x))

    (* (sqrt 30) (sqrt 30))
Something went wrong with that request. Please try again.