Search code examples
clojurelispclojurescriptclojure-core

Clojure Core function argument positions seem rather confusing. What's the logic behind it?


For me as, a new Clojurian, some core functions seem rather counter-intuitive and confusing when it comes to arguments order/position, here's an example:

> (nthrest (range 10) 5) 
=> (5 6 7 8 9)

> (take-last 5 (range 10)) 
=> (5 6 7 8 9)

Perhaps there is some rule/logic behind it that I don't see yet?

I refuse to believe that the Clojure core team made so many brilliant technical decisions and forgot about consistency in function naming/argument ordering.

Or should I just remember it as it is?

Thanks


Slightly offtopic:

rand&rand-int VS random-sample - another example where function naming seems inconsistent but that's a rather rarely used function so it's not a big deal.


Solution

  • For some functions (especially functions that are "seq in, seq out"), the args are ordered so that one can use partial as follows:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test))
    
    (dotest
      (let [dozen      (range 12)
            odds-1     (filterv odd? dozen)
            filter-odd (partial filterv odd?)
            odds-2     (filter-odd dozen) ]
        (is= odds-1 odds-2
          [1 3 5 7 9 11])))
    

    For other functions, Clojure often follows the ordering of "biggest-first", or "most-important-first" (usually these have the same result). Thus, we see examples like:

    (get <map> <key>)
    (get <map> <key> <default-val>)
    

    This also shows that any optional values must, by definition, be last (in order to use "rest" args). This is common in most languages (e.g. Java).


    For the record, I really dislike using partial functions, since they have user-defined names (at best) or are used inline (more common). Consider this code:

      (let [dozen   (range 12)
            odds    (filterv odd? dozen)
    
            evens-1 (mapv (partial + 1) odds)
            evens-2 (mapv #(+ 1 %) odds)
            add-1   (fn [arg] (+ 1 arg))
            evens-3 (mapv add-1 odds)]
    
        (is= evens-1 evens-2 evens-3
          [2 4 6 8 10 12]))
    

    Also

    I personally find it really annoying trying to parse out code using partial as with evens-1, especially for the case of user-defined functions, or even standard functions that are not as simple as +.

    This is especially so if the partial is used with 2 or more args.

    • For the 1-arg case, the function literal seen for evens-2 is much more readable to me.

    • If 2 or more args are present, please make a named function (either local, as shown for evens-3), or a regular (defn some-fn ...) global function.