Reputation: 15990
I'm sorry if this is a truly basic question, but some code I've seen got me curious.
What is the idiomatic usage of the apply
function?
For instance, I've seen code written in the form:
(distinct [1 2 3 4 5 6])
and
(apply distinct? [1 2 3 4 5 6])
These return the same result, and even in the docs, it clearly says:
;; Note the equivalence of the following two forms
user=> (apply str ["str1" "str2" "str3"]) "str1str2str3"
user=> (str "str1" "str2" "str3") "str1str2str3"
Is this example simply too basic to convey the usefulness of apply
? Or am I missing a fundamental difference between the two?
When is one form regarded as best over the other?
Upvotes: 1
Views: 172
Reputation: 26446
These are both true, but for very different reasons:
(distinct? [1 2 3 4 5 6])
;=> true
There is only one argument, the vector of 1..6, and it is distinct from any other argument because there are no other arguments
(apply distinct? [1 2 3 4 5 6])
;=> true
There are 6 arguments, all of which are distinct.
Observe:
(distinct? [1 1 1])
;=> true
There is only one argument, the vector of three 1
s
(apply distinct? [1 1 1])
;=> false
There are three arguments, all three of which are 1
.
Note the difference:
(str [1 2 3])
;=> "[1 2 3]" -- single argument of a vector stringified
(apply str [1 2 3])
;=> "123" -- three arguments each stringified and concatenated
Apply effects the transformation (apply f [a b c]) => (f a b c)
, which is generally not the same as (f [a b c])
.
Upvotes: 4
Reputation: 568
Use apply
when you want to treat a collection as the arguments of a function. In the case of distinct
it takes a collection as it's argument, so it's not necessary to use apply
.
(distinct [1 2 3 4 1 1])
;returns: (1 2 3 4)
distinct?
returns true if it's arguments are distinct:
(distinct? [1 2 3 4 1 1])
;returns true because there's only one argument
apply
uses the items in the collection as arguments:
(apply distinct? [1 2 3 4 1 1])
;returns false because of the duplicated 1's
Upvotes: 2
Reputation: 3514
Generally, I use apply
to transform a vector to arguments when calling a function. This is a lot like the apply
function found in JavaScript as shown here
Functions such as str
are variadic and expect the same type as input, in this case, anything that implements toString
. Using (str a b c)
is idiomatic, (apply str [a b c])
is not.
The function apply
can be used when you have a heterogeneous vector whose items you would like to use as arguments to a function. You may find the need to create a list of vectors where the items in the vector correspond to the arguments of your function, then it's necessary to use apply
.
I think of apply
as: exploding the vector into arguments.
Example:
(def authtypes [:basic :basic :oauth])
(def usernames ["charlie" "snoopy" "lisa"])
(def passwords ["brown" "dog" "maggie"])
(let [credentials (map vector authtypes usernames passwords)]
(doseq [c credentials]
(apply login-user c)))
Upvotes: 1
Reputation: 5879
user=> (apply str ["str1" "str2" "str3"]) "str1str2str3"
user=> (str "str1" "str2" "str3") "str1str2str3"
In this example, the advantage of using apply
is that it can take a list of strings. str
, by itself, cannot.
I'm no expert, but my instinct says that you shouldn't use apply
unless necessary. Therefore, if you have a collection of values that you want to pass to a variadic function, apply
is useful — otherwise, just use the plain function, e.g. str
.
Upvotes: 5