Search code examples
clojurefunction-compositionboot-clj

Why does the execution order of tasks in clojure boot change?


(deftask test1 "first test task" [] (print "1") identity)
(deftask test2 "second test task" [] (print "2") identity)
(boot (comp (test1) (test2)))
=> 12nil
(boot (comp (fn [x] (print "1") identity) (fn [x] (print "2") identity)))
=> 21nil

If I use comp on tasks, the execution order is from left to right. If I use comp on anonymous functions, the execution order is from right to left. How is this inconsistency reasonable?


Solution

  • The reason for that difference is that when you use comp with boot tasks is that they are not bare logic that gets composed but each boot tasks returns a function which will be invoked later and that function wraps another function which is passed to it (like in ring middleware).

    With plain functions it works like this:

    (comp inc dec)

    produces a function which does following:

    (inc (dec n))

    In boot tasks are similar to ring middleware. Each task is a function which returns another function that will wrap the next handler from the pipeline. It works like that (not literally, it's simplified for readability sake):

    (defn task1 []
      (fn [next-handler]
        (fn [fileset]
          (print 1) ;; do something in task1
          (next-handler fileset))) ;; and call wrapped handler
    
    (defn task2 []
      (fn [next-handler]
        (fn [fileset]
          (print 2) ;; do something in task1
          (next-handler fileset)))) ;; and call wrapped handler
    

    So when you do:

    (comp (task1) (task2))

    And execute such composed task it works as if it was:

    (fn [fileset1]
      (print 1)
      ((fn [fileset2]
         (print 2)
         (next-handler fileset2))
       fileset1))
    

    Because a function produces by (task2) will be passed to function produced by (task1) which will wrap the one from (task2) (and call it after printing 1).

    You can read more on boot tasks anatomy in its wiki. It might be also useful to read on ring middleware.