1

I'm new to clojure and I'm trying to understand as much as possible but the documentations are so vague

when you have a function

(fn [_ {:keys [kind]}] kind)

my understanding is that the function takes a vector map but only wants access to a key as specified by the second param {:keys..... correct?

Then how many parameters do I pass to this function? 1 or 2?

is it a vector? {:kind 1 :dog 2} or the vector key value (:kind {....})?

Kendall
  • 5,065
  • 10
  • 45
  • 70

3 Answers3

2

Your function expects two parameters (has arity 2.) The first parameter's name is _ (underscore,) which conventionally denotes a variable which is not used. (It is a valid variable name, and it becomes bound, but by convention it is not used, so it denotes a placeholder.)

The second parameter is destructured - {:keys [kind]}, which means that the expected value is a map, and as a result of destructuring a variable named kind will be bound to the value of key :kind of the actual parameter (or nil if there is no such key.)

So your function expects two parameters, the first is ignored, the second must be a map with a key :kind (maybe other keys as well, but they are ignored.)

((fn [_ {:keys [kind]}] kind) :foo {:kind :bar, :color :green})
=> :bar

P.S. I have looked at your question again, and I realised where your confusion may be coming from. The syntax [_ {:kind [kind]}] looks like a vector that includes a map at the second position. That's correct, but not everywhere in Clojure a vector notation means you can stick a vector there. Clojure users square brackets for a list of formal parameters of a function - it is part of a common pattern in Clojure of using vector literals everywhere a sequence may be expected. It is in fact very convenient, compared to other Lisps where you need to carefully use quotes in many places to avoid interpreting your lists as function application forms. But that's a digression, the important point is in fn (and defn) the square brackets denote parameter list and do not mean that the parameter is a vector.

Yuri Steinschreiber
  • 2,648
  • 2
  • 12
  • 19
0

Let's give the function a name:

(defn splot [_ {:keys [kind]}] kind)

This is shorthand for

(defn splot [_ {kind :kind}] kind)
  • a function of two (positional) arguments:

    1. _, conventionally unused, and
    2. an anonymous map, which has its :kind entry bound to local kind;
  • which then returns whatever is bound to kind.

Bindings that fail return nil.

For examples:

(splot "..." 6)            ;nil
(splot "..." {})           ;nil
(splot "..." {:kind 77})   ;77
Thumbnail
  • 13,293
  • 2
  • 29
  • 37
  • "_, conventionally unused" if it is conventionally used then why do we have it....why not just go with one argument? Is there some documentation that can explain constructing functions like these? and the purpose behind it? – Kendall Jul 27 '16 at 12:11
  • @Kendall See [here](http://stackoverflow.com/questions/16020278/clojure-what-does-do-in-a-functions-argument-list). It might be worth asking the question in general. – Thumbnail Jul 27 '16 at 13:12
  • 3
    @Kendall You would never define a function with an unused argument "in isolation", but often you must define a function whose argument list matches up to some other interface, because you pass your function as an argument to some other function, which calls your function with two arguments. For example `(map (fn [_ x] [(rand-int 10) x]) (range) xs)` is a bit of a weird expression, but shows the kind of scenario in which you might do this. – amalloy Jul 27 '16 at 17:49
-1

It's fairly straightforward to see what a given function syntax does by trying it in the repl.

+user=> ((fn [_ {:keys [kind]}] kind) (:kind {}))
ArityException Wrong number of args (1) passed to: user/eval1/fn--3  clojure.lang.AFn.throwArity (AFn.java:429)
+user=> ((fn [_ {:keys [kind]}] kind) nil (:kind {}))
nil
+user=> ((fn [_ {:keys [kind]}] kind) nil {:kind 1})
1

the {:keys []} destructuring syntax is described pretty comprehensively in the official docs, and describes a shorthand to bind values that come from a hashmap. In Clojure {} is a hashmap, and [] is a vector, and these are not interchangeable. (:foo m) invokes get to look up the value under the key :foo in m. (let [{:keys [foo]} {:foo "bar"}] ...) and (let [foo (:foo {:foo "bar"})] ...) are equivalent.

noisesmith
  • 20,076
  • 2
  • 41
  • 49
  • 1
    I don't know if I would call this straightforward. If you already know what the answer is it's easy to verify, but if you don't, you're just making random guesses and hoping one of them is legal. For example, try this one in the repl: `(fn [{{[x y] :sam} :top :keys [z]} [{:strs [a]} {b 2}]] nil)` - and remember, you're just allowed to try it in the repl, don't analyze it out by examining the destructuring form. – amalloy Jul 27 '16 at 05:19
  • I'm confused by this answer – Kendall Jul 27 '16 at 12:08