1. clojure.core/some

Freebie

Published 09 May 16

Clojure’s some function is surprisingly versatile. Find out what it does, and what you can do with it.

Clojuredocs.org has several more examples about some, which is not to be confused with that other function, some?.

This is what Compojure’s routing function looks like:

(defn routing
  "Apply a list of routes to a Ring request map."
  [request & handlers]
  (some #(% request) handlers))

This is then used by routes, and in turn by defroutes.

(defn routes
  "Create a Ring handler by combining several handlers into one."
  [& handlers]
  #(apply routing % handlers))
 
(defmacro defroutes
  "Define a Ring handler function from a sequence of routes. The name may
  optionally be followed by a doc-string and metadata map."
  [name & routes]
  (let [[name routes] (macro/name-with-attributes name routes)]
   `(def ~name (routes ~@routes))))

You can peruse the full source of compojure.core   on CrossClj

When I first came to Clojure I remember being utterly confused by the some function. The name just didn’t make sense, and I found it’s behavior non-obvious.

I think the reason is that it’s one of those general utility functions that can be used in sometimes surprising way.

Let’s start with the basics

(pos? 3)
;;=> true

(pos? -2)
;;=> false

(some pos? [-5 42 43])
;;=> true

(some pos? [-5 -6 -7])
;;=> nil

The pos? function checks if a number is positive, so greater than zero.

The some function takes as its arguments a function and a collection. In this case we pass it the pos? function, and a vector of numbers. If some of the numbers in the vector are positive, in other words, one or more of them, than some will return true.

Conversely, if none of the numbers are greater than zero, then some returns a falsy value, nil.

(or
 (pos? -5)
 (pos? 42)
 (pos? 43))
;;=> true

(or
 (pos? -5)
 (pos? -6)
 (pos? -7))
;;=> false

So that explains the name, the function must be true for some of the items. It’s like applying the function to each item, and then combining the results with a logical or. Notice how we get the exact same results for the same numbers. It seems the version with or is equivalent to the version with some.

pos? is a function that always returns true or false, but what if we use a function that returns some other value, like a string, or a map, or nil?

(or
  nil
  false
  "greetings!")
;;=> "greeting!"

(some (fn [x] x) [nil
                  false
                  "greetings!"])
;;=> "greetings!"

or will treat nil and false as logically false, also called falsy, and any other value as logically true, or truthy. The return value of or is the first truthy value it encounters. The some function is similar, it passes each value in the collection through the supplied function, and the first truthy value it gets back, it returns.

(def shelf
  [{:cake "Pineapple Cake"}
   {:tea "Jinxuan Wulong Tea"}
   {:tofu "Stinky Tofu"}])

(:tea {:cake "Jinxuan Wulong Tea"})
;;=> nil

(:tea {:tea "Jinxuan Wulong Tea"})
;;=> "Jinxuan Wulong Tea"

(some :tea shelf)
;;=> "Jinxuan Wulong Tea"

Suppose I have a friend visiting from afar. Isn’t that a joy? My friend is dying for a cup of tea. Luckily I have my shelves neatly arranged, so I look for the box labeled “tea”, and get its contents.

Remember that Clojure keywords can be invoked just like functions. Pass them a map and they will look up the value in the map that corresponds with the keyword, or nil if the keyword is not present in the map.

some will first check the cake box, but won’t find any tea there. The second box does contain tea, which some returns, so now I can brew my friend a lovely cuppa.

Now let’s look at three specific use cases for using some :

  • checking if a collection contains a certain value
  • as an alternative for apply or
  • as a way of “stacking” request handlers

Checking if a collection contains a certain value

The first use case is to check if a collection contains a certain value.

(#{:a :b} :a)
;;=> :a
(#{:a :b} :c)
;;=> nil

While learning Clojure you may have noticed that there is no generic function for checking if a collection contains a certain value. Sets do support this operation, you simply use the set as a function, and it will return the item if it is in the set, or nil otherwise.

(does-vector-include? :a [:a :b]) ;;=> :(
([:a :b] :a) ;;=> :(

Other collection types don’t support this operation, because it can’t be done efficiently, it would require a linear search through all items of the collection.

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

Sometimes that is exactly what you want though, in this case the Clojure way is to combine some with a set literal as a function.

some will go over the vector, calling the set with each value in turn. Most of the time this will return nil, but when checking the :a keyword, the set will return the keyword itself, which is truthy, so the search stops and some returns :a.

This gives us a concise and idiomatic way to check if an element is present.

alternative for “apply or”

The second interesting use case for some is as an alternative for apply or

(def v [nil false 5 nil 7])

Suppose you have a sequence of values, like this vector, and you want to find the first value that is truthy. There are several ways you could go about this.

(filter identity v)
;;=> (5 7)

(first (filter identity v))
;;=> 5

(identity 5) ;;=> 5
(identity "string") ;;=> "string"

You could filter out the falsy values, and then take the first value of the result.

Notice the use of the identity function here. It’s a function that always returns its argument. Not terribly useful by itself, but often useful when combined with “higher order functions”, functions which take another function as one of their arguments.

In this case we’re not interested in transforming the values in the vector, so they should show up in the output exactly as in the input.

Conceptually you may have noticed that this is again the same as doing a logical or, but we have the arguments in a vector, so we need to somehow “expand” them to individual arguments.

(apply or v)

The Clojure way to “expand” or “splat” a variable number of arguments would be to use apply. Unfortunately for us, that doesn’t work. or is not a regular function, it’s a macro, and so we can’t pass it to apply.

(some identity v)

Whenever you find yourself wanting to type “apply or”, type “some identity” instead. Since some works almost exactly like or, this does what “apply or” would do.

Ring route stacking

Finally let’s see how some can be use to “stack” request handlers in a web application.

(defn home-page-handler [req]
  {:status 200
   :body "Hello, home!"})

(home-page-handler {:method :get, :uri "/"})

For this example imagine you have a web app that can respond to various HTTP requests. Web applications in Clojure typically follow the Ring specification. This simply means they are functions which receive a Clojure map representing the request, and return a map representing the response.

Right now we don’t do anything with the request yet, we always return the same response, a “200 OK” status containing the text “Hello, home”.

(defn home-page-handler [{:keys [uri method] :as req}]
  (if (and (= method :get) (= uri "/"))
    {:status 200
     :body "Hello, home!"}))

(home-page-handler {:method :get, :uri "/"})
;;=> {:status 200, :body "Hello, home!"}

(home-page-handler {:method :get, :uri "/foo"})
;;=> nil

But really this response is only suitable for the home page, if people visit the about page, they need to see different content. Let’s fix this by checking the URI and request method. Clojure’s destructuring syntax makes it easy to pull these out of the request.

(defn about-page-handler [{:keys [method uri] :as req}]
  (if (and (= :get method) (= "/about" uri))
    {:status 200
     :body "All about us!"}))

(about-page-handler {:method :get, :uri "/"})
;;=> nil

(about-page-handler {:method :get, :uri "/about"})
;;=> {:status 200, :body "All about us!"}

The about page handler is identical, except that it responds to a different URI path, and returns different content.

These handlers are now a bit more selective. When calling the home page handler with the correct URI for the home page it returns a response. Any other URI will cause it to return nil.

(defn application-routes [request]
  (or
   (home-page-handler request)
   (about-page-handler request)))

(application-routes {:method :get, :uri "/"})
;;=> {:status 200, :body "Hello, home!"}
(application-routes {:method :get, :uri "/about"})
;;=> {:status 200, :body "All about us!"}

Because of this selectiveness we can now easily combine them with a logical or. If a handler ignores the request then the next one will be called, until one responds.

This pattern should look somewhat familiar by now, although it is slightly different from before.

Instead of calling the same function several times, each time with a different argument, we now call a different function each time, always with the same argument.

(defn application-routes [request]
  (some #(% request)
        [home-page-handler
         about-page-handler]))

Luckily functions are just data, so we can iterate over them as well, and use a small inline function to apply them to the request.

Now our handlers are listed in order, and it’s trivial to add new ones to the list.

(defn application-routes [request]
  ((compojure.core/routes
     home-page-handler
     about-page-handler) request))

(defroutes application-routes
  home-page-handler
  about-page-handler)

The good thing is we don’t have to implement this ourselves, because the compojure library comes with a function that does exactly this. Let’s refactor this in steps. First we use the routes function, which takes our handlers, and returns a combined handler, which we then call with the request.

This is still a bit wordy. Compojure comes with a macro called defroutes. It provides that extra bit of syntactic sugar to make this code nice and concise.

(application-routes {:uri "/" :method :get})
;;=> {:status 200, :headers {"Content-Type" "text/plain"}, :body "Hello, home!"}
(application-routes {:uri "/about" :method :get})
;;=> {:status 200, :headers {"Content-Type" "text/plain"}, :body "We are awesome, "}

And now our requests are dispatched to the right handler, just as they should.

I hope you enjoyed this deep dive into a surprising little function. Till next time!