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)
true
; Surprises people
user=> (contains? [:a :b :c] :a)
false
; More surprised looks
user=> (contains? [:a :b :c] 0)
true

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])
:a
user=> (some #{:d} [:a :b :c])
nil

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])
false

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

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

Both versions are perfectly idiomatic Clojure.

Upshot

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 .