Functions

In Clojure, a function call is simply a list whose first element resolves to a function. For example, this call to str concatenates its arguments to create a string:

 (str ​"hello"​ ​" "​ ​"world"​)
 -> ​"hello world"

Function names are typically hyphenated, as in clear-agent-errors. If a function is a predicate, then by convention, its name should end with a question mark. As an example, the following predicates test the type of their argument, and all end with a question mark:

 (string? ​"hello"​)
 -> true
 
 (keyword? :hello)
 -> true
 
 (symbol? ​'hello​)
 -> true

To define your own functions, use defn:

 (​defn​ name doc-string? attr-map? [params*] prepost-map? body)

The name is a symbol naming the function (implicitly defined within the current namespace). The doc-string is an optional string describing the function. The attr-map associates metadata with the function’s var. It’s covered separately in Metadata. The prepost-map? can be used to define preconditions and postconditions that are automatically checked on invocation, and the body contains any number of expressions. The result of the final expression is the return value of the function.

Let’s create a greeting function that takes a name and returns a greeting preceded by "Hello":

 (​defn​ greeting
 "Returns a greeting of the form 'Hello, username.'"
  [username]
  (str ​"Hello, "​ username))

You can call greeting:

 (greeting ​"world"​)
 -> ​"Hello, world"

You can also consult the documentation for greeting:

 user=>​ (doc greeting)
 -------------------------
 exploring/greeting
 ([username])
 Returns a greeting of the form ​'Hello,​ username.'

What does greeting do if the caller omits username?

 (greeting)
 -> ArityException Wrong number of args (0) passed to​:​ user/greeting
 clojure.lang.AFn.throwArity (AFn.java:429)

Clojure functions enforce their arity, that is, their expected number of arguments. If you call a function with an incorrect number of arguments, Clojure throws an ArityException. If you want to make greeting issue a generic greeting when the caller omits username, you can use this alternate form of defn, which takes multiple argument lists and method bodies:

 (​defn​ name doc-string? attr-map?
 ([params*] body)+)

Different arities of the same function can call one another, so you can easily create a zero-argument greeting that delegates to the one-argument greeting, passing in a default username:

 (​defn​ greeting
 "Returns a greeting of the form 'Hello, username.'
  Default username is 'world'."
  ([] (greeting ​"world"​))
  ([username] (str ​"Hello, "​ username)))

You can verify that the new greeting works as expected:

 (greeting)
 -> ​"Hello, world"

You can create a function with variable arity by including an ampersand in the parameter list. Clojure binds the name after the ampersand to a sequence of all the remaining parameters. There may be only one variable arity parameter, and it must be last in the parameter list.

The following function allows two people to go on a date with a variable number of chaperones:

 (​defn​ date [person-1 person-2 & chaperones]
  (println person-1 ​"and"​ person-2
 "went out with"​ (count chaperones) ​"chaperones."​))
 (date ​"Romeo"​ ​"Juliet"​ ​"Friar Lawrence"​ ​"Nurse"​)
 | Romeo and Juliet went out with 2 chaperones.

Writing function implementations differing by arity is useful. But if you come from an object-oriented background, you’ll want polymorphism, that is, different implementations that are selected by type. Clojure can do this and a whole lot more. See Chapter 9, Multimethods and Chapter 7, Protocols and Datatypes for details.

defn is intended for defining functions at the top level. If you want to create a function from within another function, you should use an anonymous function form instead.

Anonymous Functions

In addition to named functions with defn, you can also create anonymous functions with fn. At least three reasons exist to create an anonymous function:

  • The function is so brief and self-explanatory that giving it a name makes the code harder to read, not easier.

  • The function is being used only from inside another function and needs a local name, not a top-level binding.

  • The function is created inside another function for the purpose of capturing the values of parameters or local bindings.

Functions used as predicates when filtering data are often brief and self-explanatory. For example, imagine that you want to create an index for a sequence of words, and you don’t care about words shorter than three characters. You can write an indexable-word? function like this:

 (​defn​ indexable-word? [word]
  (> (count word) 2))

Then, you can use indexable-word? to extract indexable words from a sentence:

 (require '[clojure.string :as str])
 (filter indexable-word? (str/split ​"A fine day it is"​ #​"W+"​))
 -> (​"fine"​ ​"day"​)

The call to split breaks the sentence into words, and then filter calls indexable-word? once for each word, returning those for which indexable-word? returns true.

Anonymous functions let you do the same thing in a single line. The simplest anonymous fn form is the following:

 (​fn​ [params*] body)

With this form, you can plug the implementation of indexable-word? directly into the call to filter:

 (filter (​fn​ [w] (> (count w) 2)) (str/split ​"A fine day"​ #​"W+"​))
 -> (​"fine"​ ​"day"​)

There’s an even shorter reader macro syntax for anonymous functions, using implicit parameter names. The parameters are named %1, %2, and optionally, a final %& to collect the rest of a variable number of arguments. You can also use just % for the first parameter, preferred for single-argument functions. This syntax looks like this:

 #(body)

You can rewrite the call to filter with the shorter anonymous form:

 (filter #(> (count %) 2) (str/split ​"A fine day it is"​ #​"W+"​))
 -> (​"fine"​ ​"day"​)

A second motivation for anonymous functions is when you want to use a named function but only inside the scope of another function. Continuing with the indexable-word? example, you could write this:

 (​defn​ indexable-words [text]
  (​let​ [indexable-word? (​fn​ [w] (> (count w) 2))]
  (filter indexable-word? (str/split text #​"W+"​))))

The let binds the name indexable-word? to the same anonymous function you wrote earlier, this time inside the lexical scope of indexable-words. (let is covered in more detail under Vars, Bindings, and Namespaces.) You can verify that indexable-words works as expected:

 (indexable-words ​"a fine day it is"​)
 -> (​"fine"​ ​"day"​)

The combination of let and an anonymous function says the following to readers of your code: “The function indexable-word? is interesting enough to have a name but is relevant only inside indexable-words.”

A third reason to use anonymous functions is when you create a function dynamically at runtime. Earlier, you implemented a simple greeting function. Extending this idea, you can create a make-greeter function that creates greeting functions. make-greeter will take a greeting-prefix and return a new function that composes greetings from the greeting-prefix and a name.

 (​defn​ make-greeter [greeting-prefix]
  (​fn​ [username] (str greeting-prefix ​", "​ username)))

It makes no sense to name the fn, because it’s creating a different function each time make-greeter is called. However, you may want to name the results of specific calls to make-greeter. You can use def to name functions created by make-greeter:

 (​def​ hello-greeting (make-greeter ​"Hello"​))
 -> #​'user/hello-greeting
 
 (​def​ aloha-greeting (make-greeter ​"Aloha"​))
 -> #​'user/aloha-greeting

Now, you can call these functions, just like any other functions:

 (hello-greeting ​"world"​)
 -> ​"Hello, world"
 
 (aloha-greeting ​"world"​)
 -> ​"Aloha, world"

Moreover, there’s no need to give each greeter a name. You can simply create a greeter and place it in the first (function) slot of a form:

 ((make-greeter ​"Howdy"​) ​"pardner"​)
 -> ​"Howdy, pardner"

As you can see, the different greeter functions remember the value of greeting-prefix at the time they were created. More formally, the greeter functions are closures over the value of greeting-prefix.

When to Use Anonymous Functions

Anonymous functions have a terse syntax—sometimes too terse. You may actually prefer to be explicit, creating named functions such as indexable-word?. That’s perfectly fine and will certainly be the right choice if indexable-word? needs to be called from more than one place.

Anonymous functions are an option, not a requirement. Use the anonymous forms only when you find that they make your code more readable. They take a little getting used to, so don’t be surprised if you gradually use them more and more.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.227.52.7