I'm currently going through the site
Clojure for the Brave and True, which I find an excellent resource for getting acquainted with the language.
Chapter 4 describes a possible implementation of the
partial function:
(defn my-partial
[partialized-fn & args]
(fn [& more-args]
(apply partialized-fn (into more-args (reverse args)))))
The question I asked myself was:
why do we need to reverse the args?
Let's examine that!
First we create a function, to which we will apply my-partial later:
(defn test1
[arg1 arg2 arg3 arg4]
(println (str "arg1='" arg1 "' arg2='" arg2 "' arg3='" arg3 "' arg4='" arg4 "'")))
Now, let's apply my-partial to test1:
(def test1-partial (my-partial test1 "arg1" "arg2"))
(test1-partial "arg3" "arg4")
We get the expected output, i.e.:
arg1='arg1' arg2='arg2' arg3='arg3' arg4='arg4'
nil
The key is the 'into' function call:
(into '("arg3" "arg4") (reverse '("arg1" "arg2")))
Which returns:
("arg1" "arg2" "arg3" "arg4")
When we try to apply the list to the test1 function, we get the expected result:
(apply test1 (into '("arg3" "arg4") (reverse '("arg1" "arg2"))))
With the output:
arg1='arg1' arg2='arg2' arg3='arg3' arg4='arg4'
nil
Note, that vectors follow a different order when using into:
(into ["arg3" "arg4"] (reverse ["arg1" "arg2"]))
Result:
["arg3" "arg4" "arg2" "arg1"]
Indeed, when using a vector, the parameters are not passed in the expected order.
(apply test1 (into ["arg3" "arg4"] (reverse ["arg1" "arg2"])))
Result:
arg1='arg3' arg2='arg4' arg3='arg2' arg4='arg1'
nil
The different behavior is reported in an example of the documentation of
into:
; Items are conj'ed one at a time, which puts them at the head of
; the destination list
user=> (into () '(1 2 3))
(3 2 1)
; This does not happen for a vector, however, due to the behavior of conj:
user=> (into [1 2 3] '(4 5 6))
[1 2 3 4 5 6]
The reason for this difference is explained in a
stack-overlow post. According to the linked answers, lists in clojure are implemented as linked-lists, thus it is more efficient to add new elements at their beginning. In contrast, for vectors it is easier to add an element at the end: in this case, there is no need to shift the elements of the vector. In fact, if extra empty space was already reserved, the addition of a new element becomes very cheap.