Clojure - named arguments

Clojure

Clojure Problem Overview


Does Clojure have named arguments? If so, can you please provide a small example of it?

Clojure Solutions


Solution 1 - Clojure

In Clojure 1.2, you can destructure the rest argument just like you would destructure a map. This means you can do named non-positional keyword arguments. Here is an example:

user> (defn blah [& {:keys [key1 key2 key3]}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there" :key3 10)
"Hai there10"
user> (blah :key1 "Hai" :key2 " there")
"Hai there"
user> (defn blah [& {:keys [key1 key2 key3] :as everything}] everything)
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
{:key2 " there", :key1 "Hai"}

Anything you can do while destructuring a Clojure map can be done in a function's argument list as shown above. Including using :or to define defaults for the arguments like this:

user> (defn blah [& {:keys [key1 key2 key3] :or {key3 10}}] (str key1 key2 key3))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

But this is in Clojure 1.2. Alternatively, in older versions, you can do this to simulate the same thing:

user> (defn blah [& rest] (let [{:keys [key1 key2 key3] :or {key3 10}} (apply hash-map rest)] (str key1 key2 key3)))
#'user/blah
user> (blah :key1 "Hai" :key2 " there")
"Hai there10"

and that works generally the same way.

And you can also have positional arguments that come before the keyword arguments:

user> (defn blah [x y & {:keys [key1 key2 key3] :or {key3 10}}] (str x y key1 key2 key3))
#'user/blah
user> (blah "x" "Y" :key1 "Hai" :key2 " there")
"xYHai there10"

These are not optional and have to be provided.

You can actually destructure the rest argument like you would any Clojure collection.

user> (defn blah [& [one two & more]] (str one two "and the rest: " more))
#'user/blah
user> (blah 1 2 "ressssssst")
"12and the rest: (\"ressssssst\")"

You can do this sort of thing even in Clojure 1.1. The map-style destructuring for keyword arguments only came in 1.2 though.

Solution 2 - Clojure

In addition to Raynes' excellent answer, there is also a macro in clojure-contrib that makes life easier:

user=> (use '[clojure.contrib.def :only [defnk]])
nil
user=> (defnk foo [a b :c 8 :d 9]
[a b c d])
#'user/foo
user=> (foo 1 2)
[1 2 8 9]
user=> (foo 1 2 3)
java.lang.IllegalArgumentException: No value supplied for key: 3 (NO_SOURCE_FILE:0)
user=> (foo 1 2 :c 3)
[1 2 3 9]

Solution 3 - Clojure

As of Clojure version 1.8, keyword support still seems a bit meh.

You can specify keyword arguments like this:

(defn myfn1
  "Specifying keyword arguments without default values"
  [& {:keys [arg1 arg2]}]
  (list arg1 arg2))

Examples of calling it:

(myfn1 :arg1 23 :arg2 45)  --> evaluates to (23 45)
(myfn1 :arg1 22)           --> evaluates to (22 nil)

If you want to specify default values for these keyword arguments:

(defn myfn2
  "Another version, this time with default values specified"
  [& {:keys [arg1 arg2] :or {arg1 45 arg2 55}}]
  (list arg1 arg2))

This does the expected thing in the second case:

(myfn2 :arg1 22)           --> evaluates to (22 55)

There are pros and cons to each part of each language, but just for comparison, this is how you would do the same stuff in Common Lisp:

(defun myfn3
    (&key arg1 arg2)
    "Look Ma, keyword args!"
    (list arg1 arg2))

(defun myfn4
    (&key (arg1 45) (arg2 55))
    "Once again, with default values"
    (list arg1 arg2))

Solution 4 - Clojure

Do you perhaps mean named parameters? These aren't directly available, but you can use this vectors approach if you like, which may give you what you want.

At RosettaCode there's a deeper explanation on how to do this using destructuring.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionone-zero-zero-oneView Question on Stackoverflow
Solution 1 - ClojureRayneView Answer on Stackoverflow
Solution 2 - ClojureAlex TaggartView Answer on Stackoverflow
Solution 3 - ClojureagamView Answer on Stackoverflow
Solution 4 - ClojureAbelView Answer on Stackoverflow