Clojure: A few things I've been tripping up on
In my continued playing with Clojure I'm noticing a few things that I keep getting confused about.
The meaning of parentheses
Much like Keith Bennett I'm not used to parentheses playing such an important role in the way that an expression gets evaluated.
As I understand it if an expression is enclosed in parentheses then that means it will be evaluated as a function.
For example I spent quite a while trying to work out why the following code kept throwing a class cast exception:
(if (true) 1 0)
If you run that code in the REPL you'll get the following exception because 'true' isn't a function and therefore can't be applied as such:
java.lang.ClassCastException: java.lang.Boolean cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0)
If we don't want something to be treated this way then the parentheses need to disappear!
(if true 1 0)
Truthyness
Somewhat related to the above is understanding which expressions evaluate to 'true' or 'false'.
I'm told there are some edge cases but that as a general rule everything except for 'false' and 'nil' evaluates to true.
I think that's an idea which is more common in languages like Ruby but I'm not yet used to the idea that we can something like this and have it execute:
(if "mark" 1 0)
In C# or Java I would except to have to compare "mark" to something in order for it to evaluate to a boolean result.
It seems like a really neat way to reduce the amount of code we have to write though so I like it so far.
Character Literals
I've been working through Mark Volkmann's Clojure tutorial and in one example he defines the following function:
(def vowel? (set "aeiou"))
I wanted to try it out to see if a certain character was a vowel so I initially did this:
=>(vowel? "a") nil
"a" is actually a string though which means it's an array of characters when what we really want is a single character.
I thought the following would be what I wanted:
(vowel? 'a')
Instead what I got was the following exception:
java.lang.Exception: Unmatched delimiter: )
This one just turned out to be a case of me not reading the manual very carefully and actually the following is what I wanted:
=> (vowel? \a) \a
Regarding "edge cases" for truthiness, http://clojure.org/special_forms#if gives the full details. The potential gotcha is covered there: all instances of class java.lang.Boolean other than the canonical false instance Boolean/FALSE are logically true.
; clojure false is Boolean/FALSE
user=> (identical? false Boolean/FALSE)
true
; other Boolean instances are logically true
user=> (Boolean. false)
false
user=> (if (Boolean. false) "truthy" "not truthy")
"truthy"
; always use Boolean/valueOf rather than the constructor
user=> (Boolean/valueOf false)
false
user=> (if (Boolean/valueOf false) "truthy" "not truthy")
"not truthy"
; clojure.core/boolean will fix up any Boolean instance
user=> (if (boolean (Boolean. false)) "truthy" "not truthy")
"not truthy"
; so will Boolean/valueOf
user=> (if (Boolean/valueOf (Boolean. false)) "truthy" "not truthy")
"not truthy"
user=>
Pure Clojure code will always produce and consume canonical false. This subtlety only comes up when using Java interop either directly (calling a Boolean constructor, something you can and should always avoid) or when interfacing with Java libraries. When in doubt about a Boolean value returned from Java, casting using either clojure.core/boolean or Boolean/valueOf will ensure no surprises.
Note that many (most?) Java libraries will return a boolean (primitive) value rather than a Boolean (object) value. Primitive false always works without surprises.
user=> (identical? Boolean/FALSE (.booleanValue (Boolean. false)))
true
Steve Gilardi
22 Nov 09 at 5:01 am