1

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?

pcalcao
  • 15,237
  • 1
  • 40
  • 62
  • Presumably `apply` has utility beyond this example. Can the other five examples in the [Clojure documentation](http://clojuredocs.org/clojure_core/clojure.core/apply) be converted into forms that do not use `apply` without making them more complex or less performant, or eliminating features? – Robert Harvey Mar 20 '13 at 15:02
  • Thinking a bit more about the examples, I see the usefulness when treating a collection as individual arguments passed to the function. I think I was too hasty in my evaluation. – pcalcao Mar 20 '13 at 15:07
  • 1
    Some cases where `apply` is needed or wanted (1) unknown variadic arguments `(apply + (range (rand-int 42)))`, (2) mapping a function to a collection of collections `(map (partial apply +) [[1 2][3 4]])`, (3) avoiding explicit destructing when arguments arrive in a collection `(= (let [pair [7 3]] (apply mod pair)) (let [[a b] [7 5]] (mod a b))`, ... – A. Webb Mar 20 '13 at 15:49

4 Answers4

5
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.

Matthew H
  • 5,521
  • 7
  • 44
  • 77
4

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]).

A. Webb
  • 25,272
  • 1
  • 57
  • 88
  • He used `distinct`, not `distinct?` in his first case. Not sure whether that was a typo or not. – Matthew H Mar 20 '13 at 15:21
  • 1
    You're right, being a complete newbie to Clojure, I did an additional mistake, messing up `distinct` and `distinct?`, two different functions... thanks for pointing it out. – pcalcao Mar 20 '13 at 15:26
2

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
sethev
  • 538
  • 3
  • 5
1

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)))
Community
  • 1
  • 1
mchlstckl
  • 2,892
  • 2
  • 20
  • 19