Today it’s clear that representing optional values with Null was not the best idea. Several modern languages model this problem with explicit nullable types or Option Monad (after the Haskell Maybe Monad). As Option in Scala has influenced my programming style heavily over the last years, I wanted to compare how other languages deal with this. I did not include Groovy although it was one of the first (the first?) JVM language to safely dereference null values because I currently have no interest in Groovy.
The languages I compare in code fragements are Scala, Kotlin, Ceylon and Swift.
Set value (languages may infer type, added type for clarity)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//Scala: Some/None of type Option[String] var h:Option[String] = "Hello".some //Kotlin: Value and Null var h:String? = "Hello" //Ceylon: Value and Null String? h = "Hallo" //Nice: Value or Null ?String h = "Hello" //Fantom: Value or Null Str? h := "Hello" //Swift: Some/None/Nil of type Optional<String> var h:String? = "Hello" var h:Optional<String> = Optional("Hello") |
Accessing values
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//Scala: Some(Value) or None val l = h.map(_.length) //Kotlin: Value or Null val l = h?.length() //Ceylon: Value or Null Integer l = h?.length //Nice: Value or Null let l = (h == null) ? 0 : h.length(); //Swift: Optional(Value) or None/Nil var l = h?.length() |
Forcing to get a value
|
1 2 3 4 5 6 7 8 9 |
//Scala: get may throw exception h.get().length() //Kotlin: may throw NPE h!!.length() //Swift: ? h!.length() |
Else values for empty/Null
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Scala h.map(_.length).getOrElse(-1) //Kotlin val l = if (h!= null) h.length() else -1 val l = h?.length() ?: -1 //Ceylon Integer l = h?.length else -1 // Ceylon: Alternative 'else' String greeting = (h else "Hello") + " there!" |
Chaining
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Scala //flatMap or map depends on call1, call2 h.flatMap(_.call1).flatMap(_.call2) //Kotlin h?.call1()?.call2() //Ceylon h?.call1()?.call2() //Swift h?.call1()?.call2() |
Alternative chaining
|
1 2 3 4 5 6 |
//Scala val name = firstName.orElse(lastName).getOrElse("Guest") // Ceylon: Chain 'else' String name = firstName else lastName else "Guest" |
Interesting in Kotlin
|
1 2 3 |
//Operator can return from method val hello = h ?: return null |
Interesting in Swift
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//Dereference a value if let l = h?.length { println("Length: \(l)") } else { // was empty } // when getPerson returns Optional if let person = getPerson { person.name = "Code Monkey" person.age = 99 } |
Interesting in Scala, see For
|
1 2 3 4 5 6 7 8 9 10 11 12 |
// h and name are Options // Returns Option[String] // Returns None if any Option is None // for is more powerful than this // as this is a simple example for ( hello <- h; name <- n ) yield { hello + ", " + name } // templating did not work in Wordpress |
Some thoughts
To me it looks like the combination of special syntax (Type?, !. and ?.) together with a Optional type in Swift looks clearer than Scala code for some cases but is more powerful and has clearer types than modeling optional in Kotlin/Ceylon (e.g. usage of flatMap chaining, flatten, Applicatives etc.). Interesting to see !! in Kotlin visually signaling that this is a dangerous operation. Ceylon else looks really nice for alternative chains, Kotlin return for the alternative case looks nice for method guards.
If you have feedbacks about things I’ve got wrong or forgot reply to @codemonkeyism or start a Hackernews thread.