Skip to content

Establishing fact wide prerequisites

marick edited this page Feb 18, 2013 · 7 revisions

It's often the case that you have prerequisites that you want to apply to many predictions. As a simple example, consider this:

(fact
  (function-under-test 1 1) => 3
  (provided
    (x-handler 1) => 1                ;; same topic
    (y-handler 1) => 2)               ;; duplicate

  (function-under-test 8 1) => 1
  (provided
    (x-handler 8) => -1               ;; same topic
    (y-handler 1) => 2))              ;; duplicate

Look first at the lines marked "duplicate". They're identical. As such, they distract from what's special about each prediction. So let's "extract" them so that they apply to all predictions in the fact:

(fact
  (prerequisite (y-handler 1) => 2)     ;; <<== new

  (function-under-test 1 1) => 3
  (provided
    (x-handler 1) => 1)
    
  (function-under-test 8 1) => 1
  (provided
    (x-handler 8) => -1))

That helps make it clear that all the predictions in this fact depend on a particular property of y-handler.

There's something more that can be done. This fact is about how function-under-test depends on both x-handler and y-handler. As such, it might be helpful to group the x-handler's different prerequisites together so that, when we code it, its properties (and their regularities) are presented to us as a checklist we have to implement. That could look like this:

(fact
  (prerequisites (y-handler 1) => 2
                 (x-handler 1) => 1     ;; <<== new
                 (x-handler 8) => -1)   ;; <<== new

  (function-under-test 1 1) => 3
  (function-under-test 8 1) => 1)

(Note that you can use either prerequisite or prerequisites. We at [MidjeCo|http://exampler.com/contact.html] are very respectful of grammatical number.

Nesting and ordering

Prerequisites apply to any facts included in the fact where they're defined.

(fact "prerequisites can be nested"
  (prerequisite (x-handler 1) => 8000)
  (fact 
    (prerequisite (y-handler 1) => 80)
    (function-under-test 1 1) => 8080)

  (fact
    (prerequisite (y-handler 1) => -8000)
    (function-under-test 1 1) => 0))

When there are matches for a prerequisite at different nesting levels, the innermost takes precedence. provided prerequisites override any from a prerequisites form.

(fact "prerequisites can be nested"
  (prerequisites (x-handler 1) => 10
                 (y-handler 1) => 8)
  (fact 
    (prerequisite (y-handler 1) => 33)

    (function-under-test 1 1) => (+ 10 33)

    (function-under-test 1 1) => (+ 10 99)
    (provided
      (y-handler 1) => 99)))

When more than one prerequisite at the same nesting level match, it's the latest one that's chosen. That allows for "catch all" or default prerequisites:

(fact "catch-all or default prerequisites"
  (prerequisites (x-handler anything) => 1
                 (y-handler anything) => 2
                 (y-handler 3) => 333)
  (function-under-test 1 1) => (+ 1 2)
  (function-under-test 1 3) => (+ 1 333))

Note that the match is not the most specific. If the order of y-handler prerequisites were switched, y-handler would always return 2.

Lexical scoping

prerequisite expressions have access to lexically-scoped symbols:

(let [my-favorite-argument-value 1
      my-favorite-expected-value 32000]
  (fact "lexical scoping is obeyed"
    (prerequisites (x-handler my-favorite-argument-value) => my-favorite-expected-value
                   (y-handler my-favorite-argument-value) => my-favorite-expected-value)

    (function-under-test my-favorite-argument-value my-favorite-argument-value)
    => (* 2 my-favorite-expected-value)))

There may conceivably be a use for that.

Clone this wiki locally