March 30, 2019

Question marks in Clojure

Clojure’s flexible syntax allows special characters such ? , ! or * to be a part of variable names, which gives ability to enhance symbols with additional meaning, but I regularly see that ? -suffixes get misused, so I decided to make a list of dos and don’ts regarding question marks to clear that up.

Do: Predicates

By convention question mark suffix should be used for predicates only. It is like that in clojure core, and, if you like Appeal to Authority arguments, Stuart Holloway also said so. Example:

( some? maybe-sheep )

Don’t: Booleans

Vars and bindings containing boolean values should not end with question mark:

( let [ disabled? ( empty? options )] ( if disabled? ;; meh ( do-nothing ) ( do-something options )))

It’s simple to understand what’s wrong here: disabled? is not a predicate. Predicate is a question, calling a predicate is asking a question and getting answer, and disabled? in this case is an answer. Answer to the question is affirmative: we know for sure that it is either true or false , so there is nothing questionable here. Also, using question mark here introduces confusion, because now we have symbols ending with ? that are either functions or booleans.

Don’t: Keywords

With keywords situation starts to get blurry, because they can be invoked as functions, so it is tempting to put question mark at the end of a keyword:

( filter :online? users ) ;; okay?..

I thinks this is a bad idea, because even though being a function is very valuable for keywords, their main use case is being identifiers for data. Using keywords as identifiers is affirmative, there are no questions here, only assertions of facts:

( def system-user { :login "system" :online? true }) ;; meh

Also, if you destructure map with such keys, you will again end up in a situation where you have a boolean binding ending with ? :

( let [{ :keys [ login online? ]} user ] [ :div ( str login ( when online? ;; meh " ●" ))])

In my experience I’ve seen more uses of such keywords as data identifiers in destructurings than as predicates.

Rarely Do: Keywords

I think an exception here could be a keyword that identifies predicate function:

{ :type :goblin :will-attack? # ( > 10 ( :strength % ))}

Destructuring it will give you a ? -ending predicate that you can call and get an answer, which makes sense.

What do you think? Discuss on reddit.