On Clojure

March 5, 2010

Pre- and post-conditions: a quest for a nicer syntax

Filed under: Uncategorized — khinsen @ 6:15 pm

When pre- and post-conditions appeared in Clojure (since version 1.1 they are “official”), I though that was a pretty neat feature and that I ought to use it. I added conditions to a couple of functions and was satisfied. But rather soon I noticed that I used conditions very sparingly, only where I expected wrong data to be fed into a function. Considering that bugs also happen where we don’t expect them, and that conditions are great documentation as well as error-checking code, I should use them more. So why don’t I?

Something I didn’t like about pre- and post-conditions is the rather heavy syntax. For short functions, the conditions take up more space than the code itself. Moreover, parsing them visually takes some effort as well, as much as reading the code itself. Wouldn’t it be nice if preconditions could be written somehow right in the argument list, and postconditions at the level of the function definition?

Well, this is Lisp, so if you don’t like some syntax, you just roll your own. I didn’t come up with a better general syntax though, but I think that what I describe below is much nicer and suitable for 90% of pre- and post-conditions used in practice. The main limitation is that each condition can depend on only one argument, or on the return value. For the other cases, there is still Clojure’s general syntax, which is perfectly compatible with my extension. For those who want to play with this themselves, here is the code.

As a first example, here’s a pretty stupid algorithm to calculate integer powers of a number:

(defn (number?) power
  [(number?) x
   (integer?) (pos?) n]
  (apply * (repeat n x)))

The preconditions check that the first argument is a number and that the second one is a positive integer. The postcondition checks that the result is a a number – the utility of this test is a bit dubious, but it serves as an illustration. Note that you can have multiple conditions per argument, and also multiple postconditions. The full form representing the condition is constructed by inserting the argument to be tested in the second position of the supplied list. The above function definition actually expands to

(defn power
  ([x n]
   {:pre [(number? x) (integer? n) (pos? n)], :post [(number? %)]}
   (apply * (repeat n x))))

One precondition in the above example is actually too strict. The argument n needn’t be positive, just non-negative. There is not simple test function for “non-negative” in Clojure, but with the above rule we can write this as:

(defn (number?) power
  [(number?) x
   (integer?) (>= 0) n]
  (apply * (repeat n x)))

Another possibility is to use the -> macro:

(defn (number?) power
  [(number?) x
   (integer?) (-> neg? not) n]
  (apply * (repeat n x)))

Preconditions can be combined with destructuring. Here is a variant of Clojure’s function second that actually verifies that its argument has at least two element:

(defn my-second
  [[f & (seq) r]]
  (first r))

There is however one limitation: I couldn’t find a way to use my new syntax with map destructuring. So for now at least it works only with vector destructuring.

Comments on this syntax are welcome. Do you like it? Can you come up with something better? Or do you think that Clojure’s standard syntax is just fine?

About these ads

9 Comments »

  1. I write a post about fn/and and fn/or function combinators http://zahardzhan.posterous.com/-fnand-fnor-fnnot
    Pre and post conditions is full of implicit fn/and. What do you think about it?

    And, also, there are toooo much parenthesis in your syntax. It should be something like that:
    (defn power :is number?
    [x :is number?
    y :is (pos? integer?)
    n :is (or (pos? integer?)
    (neg? float?))]
    (apply * (repeat n x)))

    Comment by zahardzhan — March 6, 2010 @ 3:26 am

    • Or it’s better to add some sort of ‘destructuring’ for variables http://gist.github.com/323481

      (defn (power :is number? :or #(throw ‘Bad-input))
      [(x :is number?)
      (y :is [pos? integer?])
      (n :is [pos? integer?] :is [neg? float?] :is [zero? double?] :or false)]
      (/ x 0))

      Comment by zahardzhan — March 6, 2010 @ 3:46 am

    • With your parenthesis-free syntax, how do you handle conditions that need parameters? As I see it, you’d have to define an anonymous literal each time.

      Concerning your post about function combinators, my Russian isn’t quite up to it I am afraid!

      Comment by khinsen — March 6, 2010 @ 5:50 pm

      • “Destructuring” can be used like this, with [(automaticaly generated anonymous functions here %)]

        (n
        :is [pos? integer? (or (> 10 %) (< 5 %))]
        :is [neg? float?] ;; other :is act like or-clauses
        :is [zero? double?]
        :is (identical? 3 %)
        :is number?
        :type String ;; type declaration for optimization
        :ignore ;; like in Common Lisp
        :or 0) ;; when checking failed set n to 0

        And, i think there must be way to write predicates without []-parens around.

        You may try to read some code in my post in russian – there so few lines of code.

        Comment by zahardzhan — March 6, 2010 @ 10:25 pm

  2. I like that =)

    (And I prefer the “parenthetic” version)

    Comment by bsteuber — March 6, 2010 @ 9:00 am

  3. [...] Pre- and post-conditions: a quest for a nicer syntax [...]

    Pingback by Destillat #11 | duetsch.info - Open Source, Wet-, Web-, Software — March 8, 2010 @ 12:16 pm

  4. I like the idea, but I have some reservations.

    First, there is a case you haven’t mentioned: precondition predicates that operate on more than one argument. You could specify that the predicates are all executed in an environment where all arguments are bound.

    But I kind of like having the contract separate. It’s easier to add and remove if you no longer want it.

    And parsing the argument list becomes more complicated for the compiler and the human reader. Keeping them separate is my preference.

    Eric

    Comment by Eric Normand — March 21, 2010 @ 4:04 pm

    • You are right about preconditions that depend on several arguments, but I think this is a minor point: I am not trying to replace the general syntax for preconditions, all I want is to add another one for frequent special cases. Generality is thus not very important.

      As for separating the contract from the argument list, that’s actually what I am trying to avoid. For me the contract is as much part of the interface as the number of arguments or the function name. I may want to turn off the verification of the contract, for performance reasons, but that can be achieved by disabling assertions. I don’t imagine not wanting the contract any more.

      However, I do agree with your point about readability. The conditions tend to take up much more space than the argument they refer to, making it more difficult to spot the argument visually. This could probably be solved by syntax-coloring in the editor, though I don’t expect all editors and IDEs to implement my personal syntax.

      Comment by khinsen — March 22, 2010 @ 4:03 pm

  5. I think your looking for something like this: http://github.com/fogus/trammel

    Look at his blog too!

    Comment by nickik — July 16, 2010 @ 12:13 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: