8

I am learning Clojure, and I saw this bit of code online:

(count (filter #{42} coll))

And it does, as stated, count occurrences of the number 42 in coll. Is #{42} a function? The Clojure documentation on filter says that it should be, since the snippet works as advertised. I just have no idea how it works. If someone could clarify this for me, that would be great. My own solution to this same thing would have been:

(count (filter #(= %1 42) coll))

How come my filtering function has parenthesis and the snippet I found online has curly braces around the filtering function (#(...) vs. #{...})?

vim
  • 247
  • 2
  • 8
  • Have you seen this answer? http://stackoverflow.com/questions/3249334/test-whether-a-list-contains-a-specific-value-in-clojure – Jonas May 03 '14 at 18:13
  • No, I have not seen that answer. I can see that they are also using similar bits of code. There is also mention that `(#{x} x)` returns `x`. I just don't get what `#{x}` is doing as a function. Isn't that just a set? – vim May 03 '14 at 18:24
  • See my answer. A set is not just a set - amongst other things it's an IFn so you can treat it as a function. – pete23 May 03 '14 at 18:25
  • 2
    This is tangentially related, but in case you weren't aware, Clojure also has a function called [frequencies](http://clojuredocs.org/clojure_core/clojure.core/frequencies) that is useful for cases like this. It produces a map of each item in a collection to the number of times it occurs. So you could do the above like this: `((frequencies coll) 42)` -- `(frequencies coll)` produces a map, which is used as a function to look up `42` and return the number of occurrences. (EDIT: To be fair, this is not as performant) – Dave Yarwood May 05 '14 at 15:02

2 Answers2

16
=> #{42}
#{42}

Defines a set...

=> (type #{42})
clojure.lang.PersistentHashSet

=> (supers (type #{42}))
#{clojure.lang.IHashEq java.lang.Object clojure.lang.IFn ...}

Interestingly the set implements IFn so you can treat it like a function. The behaviour of the function is "if this item exists in the set, return it".

=> (#{2 3} 3)
3
=> (#{2 3} 4)
nil

Other collections such as map and vector stand in as functions in a similar fashion, retrieving by key or index as appropriate.

=> ({:x 23 :y 26} :y)
26
=> ([5 7 9] 1)
7

Sweet, no? :-)

pete23
  • 2,204
  • 23
  • 28
1

Yes, #{42} is a function,

  • because it's a set, and sets, amongst other capabilities, are functions: they implement the clojure.lang.IFn interface.
  • Applied to any value in the set, they return it; applied to anything else, they return nil.
  • So #{42} tests whether its argument is 42 (only nil and false are false, remember).

The Clojure way is to make everything a function that might usefully be one:

  • Sets work as a test for membership.
  • Maps work as key lookup.
  • Vectors work as index lookup.
  • Keywords work as lookup in the map argument.

This

  • often saves you a get,
  • allows you, as in the question, to pass naked data structures to higher order functions such as filter and map, and
  • in the case of keywords, allows you to move transparently between maps and records for holding your data.
Thumbnail
  • 13,293
  • 2
  • 29
  • 37