Truth and languages

The title of this paragraph sounds a bit postmodern. But here, we want to discuss the truth in Clojurescript and javascript .

Javascript ’s conception of the truth is a bit surprising. See by yourself:

Boolean ( 0 ) // false Boolean ( " 0 " ) // true 0 == " 0 " // true 0 == false // true " 0 " == false // true Boolean ( "" ) // false Boolean ([]) // true Boolean ({}) // true Boolean ( Math . sqrt ( - 1 )) // false

In any “normal” programming language, 0 and "" should be truthy. You could argue about Math.sqrt(-1) …

On the opposite, Clojure ’s conception of the truth is completly well defined.

It is therefore very interesting to ask:

How Clojurescript handles the truth?

Clojurescript: a teacher about truth

Let’s look at some transpiled javascript code with KLIPSE in order to understand how clojurescript checks if something is true:

(defn check [x] (if x "true" "false"))

You see in the transpiled javascript code that the x variable has been wrapped into into a call to the cljs.core.truth_ function.

Here is the code for cljs.core.truth_ :

function cljs$core$truth_ ( x ) { return x != null && x !== false }

This is how clojurescript teaches javascript what is true and what is not - in its own language!

And indeed, javascript is a good student

cljs$core$truth_ ( true ) // true cljs$core$truth_ ( false ) // false cljs$core$truth_ ( 0 ) //true cljs$core$truth_ ( "" ) // true cljs$core$truth_ ( null ) // false cljs$core$truth_ ( undefined ) // false cljs$core$truth_ ( NaN ) // true cljs$core$truth_ ( Math . sqrt ( - 1 )) // true

Performances

It’s nice to have a truth wrapper. But what if you are in a performance sensitive environment and you want to use the native javascript truth system - in order to move faster?

Well, clojurescript provides a way to let the compiler know that you trust javascript : the ^boolean type hint.

Let’s see it in action with KLIPSE:

(defn check [^boolean x] (if x "true" "false"))

By using the ^boolean type hint, you let the compiler know that x must be a boolean i.e. either true or false . In that case, it’s safe to trust javascript about the truth. There is no need to wrap the hinted variable into cljs.core.truth_ .

And the clojurescript compiler is smart: it knows how to propagate type hints i.e. if you assign a hinted variable x into an unhinted variable y , then y is automatically hinted.

Let’s check it with a simple piece of code in KLIPSE:

(defn check [^boolean x] (let [y x] let(if y "true" "false")))

You see that y has not been wrapped.

Cool, isn’t it?

Dangers

With great power comes great responsibility

Let’s have a look at some interesting edge cases exposed by Mike Fikes involving the ^boolean type hint:

(defn f [^boolean b] (loop [x b n 0] (cond (= n 100000) "almost infinite loop" (not x) (recur 0 (inc n)) :else :done))) (f false)

What’s happened here?

Remember that in javascript , 0 is falsy.

b is declared as a boolean x is also considered as a boolean because of type propagation: x is not wrapped into cljs.core.truth_ f is called with a boolean value: false So far so good… But f breaks the contract as it assigns a non-boolean value 0 into x .

Therefore, x is not wrapped, and we are in a non-safe situation: a non-boolean value is handled by the javascript truth system.

Let’s follow, the flow of the loop for the first two iterations:

First iteration: x=false and n=0 ; false is falsy ➠ recur with x=0 and n=1 Second iteration: x=0 and n=1 ; 0 is falsy ➠ recur with x=0 and n=2 …. …

…





How to get the best of the two worlds?

Clojurescript provides a way to turn off type inference, using the ^any type hint.

Let’s add ^any to x and see how it solves our problem:

(defn f [^boolean b] (loop [^any x b n 0] (cond (= n 100000) "almost infinite loop" (not x) (recur 0 (inc n)) :else :done))) (f false)

Hourra! x has been wrapped again and we are safe.

And obviously, it solves the infinite loop issue:

(defn f [^boolean b] (loop [^any x b n 0] (cond (= n 100000) "almost infinite loop" (not x) (recur 0 (inc n)) :else :done))) (f false)

Clojurescript rocks!