pcalcao
pcalcao

Reputation: 15990

Idiomatic usage of apply in Clojure

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

Answers (4)

A. Webb
A. Webb

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 1s

(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

sethev
sethev

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

mchlstckl
mchlstckl

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

Matthew H
Matthew H

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

Related Questions