Did you know about some?

Every once in a while someone asks on the Clojure group why (contains? [:a :b :c] :a) returns false. In the following heated discussions about semantics (yeah! a discussion about semantics!) people always suggest to add a linear-search to Clojure. What they miss: linear-search is already there.

The confusion

The confusion with contains? is explained quite simply: people expect it to work on keys with maps and on values with vectors.

; Matches expectations
user=> (contains? {:a 1 :b 2 :c 3} :a)
; Surprises people
user=> (contains? [:a :b :c] :a)
; More surprised looks
user=> (contains? [:a :b :c] 0)

However, reading the last sentence of the previous paragraph should already explain the above behaviour: vectors are keyed by number, not the item value! So how can contains? work as people expect without being completely inconsistent with its contract?

The clarification

So contains? does not a linear search. However another function does: some. A perfectly normal, idiomatic Clojure version of the desired behaviour is the following:

user=> (some #{:a} [:a :b :c])
user=> (some #{:d} [:a :b :c])

This will do the trick. With one little exception: in case boolean false and nil are valid values in our collection we are in trouble.

user=> (some #{false} [:a false :b])

Doh! In this case we have to use #(= % false):

user=> (some #(= % false) [:a false :b])

Both versions are perfectly idiomatic Clojure.


Today we will close with the words of Chris Houser who puts things quite on the spot.

What is the drawback of the (some #{:y} [:x :y :z]) idiom? Is it too verbose? Too slow? Too flexible? Too good a re-use of existing functionality? Too helpful in opening ones eyes to the possibilities of sets and higher order functions?

Published by Meikel Brandmeyer on .