Download as pdf or txt
Download as pdf or txt
You are on page 1of 45

escape from

null hell
Gavin Bong
gavin.emploi@gmail.com

http://raverun.com/license/2008/nullhell
motivation

Our code is littered with


runtime failure cases.

Yet the compiler remains


silent.

Tim Sweeney.
2006
Epic games (gears of war). talk on next mainstream language. 2 failures: concurrency and reliability.
2009 face with 20+ cores & 80+ hw threads. 50% unreal engine bugs => runtime failures e.g. outofbounds array
null pointer dereferences. Inherent in computer memory, pointer/reference to nothing. Code acrobatics to avoid ..
ruby

irb(main):018:0> num.length
NoMethodError: undefined method `length'
for nil:NilClass
from (irb):18:in `Kernel#binding'
from :0
C/C++

$> man woman


$> Segmentation fault: core dumped

Thinkgeek.com copyright
java

java.lang.NullPointerException
at android.app.ActThread.performLaunchActivity
(ActThread.java:17)
at android.app.ActThread.handleLaunchActivity
(ActThread.java:20
at android.app.ActThread.access$1500
(ActThread.java:99
at android.app.ActThread$H.handleMessage
(ActThread.java:1271)
at android.os.Handler.dispatchMessage(Handler.java:80)
solution #1: practises

Null Object
Pattern

don't return null, return empty arrays, empty lists. HOWEVER, vendor frameworks => obliged to check
solution #1: practises

local variables

non null assigned during declaration. If your language supports it; variables can be limited to a block of code. Lexical scoping. Generalization
of the rule of thumb to reduce the scope of variables. Scope is fixed during compile time.
haskell

breedSheep = let adam = Sheep "Adam" Nothing Nothing


eve = Sheep "Eve" Nothing Nothing
thierry = Sheep "Thierry" (Just eve) (Just adam)
edith = Sheep "Edith" (Just eve) (Just adam)
cecile = Sheep "Cecile" (Just edith) (Just thierry)
in Sheep "Dolly" (Just cecile) Nothing
haskell

breedSheep = let adam = Sheep "Adam" Nothing Nothing


eve = Sheep "Eve" Nothing Nothing
thierry = Sheep "Thierry" (Just eve) (Just adam)
edith = Sheep "Edith" (Just eve) (Just adam)
cecile = Sheep "Cecile" (Just edith) (Just thierry)
in Sheep "Dolly" (Just cecile) Nothing

ruby with ICK

let( find(:first) ) { |person| person.name if person }


solution #2 : improved null/nil

nil.to_a
nil.to_yaml ruby

nil.nil?

syntactic sugar
solution #2 : improved null/nil

nil.to_a
nil.to_yaml ruby

nil.nil?

messaging to Objective C

nil object OK.


solution #3 : nullable types

annotate

static compile-time verification, database NULL/notNULL columns


String f = "hello";
println( f.length() );

No null guards needed. NPE never thrown.


Nice
String f = "hello";
println( f.length() );

?String g = "hello";
println( (g==null) ? 0 : g.length() );

One documents whether a type can hold the null value by simply prefixing it with the ?
null checks not completely gone Nice
String f = "hello";
println( f.length() );

?String g = "hello";
println( (g==null) ? 0 : g.length() );

?String h = "hello";
println( h.length() );

interop with java


Nice
int? age = ..
int? temp = age ?? -1;

is a syntactic sugar for ... next slide


C# 2.0+
int? age = ..
int? temp = age ?? -1;

System.Nullable<int> age = ..

if (age.HasValue)
{
System.Console.WriteLine(age.Value);
}

type param can be primitives & structs. Not reference types.


C# 2.0+
Option Types in FP

data Maybe a = Just a | Nothing

Ocaml no nulls, Explicit possible failure, Algebraic Data Type, Error Monad, parametric polymorphic, 2
constructors, Just is boxing
haskell
data Sheep = Sheep { name::String,
mom::Maybe Sheep,
dad::Maybe Sheep
}

breedSheep :: Sheep
breedSheep = let adam = Sheep "Adam" Nothing Nothing
eve = Sheep "Eve" Nothing Nothing
thierry = Sheep "Thierry" (Just eve) (Just adam)
edith = Sheep "Edith" (Just eve) (Just adam)
cecile = Sheep "Cecile" (Just edith) (Just thierry)
in Sheep "Dolly" (Just cecile) Nothing

main :: IO ()
main = let dolly = breedSheep
in do { print (mom dolly);
print (dad dolly);
print (maternalGrandfather dolly);
}

haskell
data Sheep = Sheep { name::String,
mom::Maybe Sheep,
dad::Maybe Sheep
}

breedSheep :: Sheep
breedSheep = let adam = Sheep "Adam" Nothing Nothing
eve = Sheep "Eve" Nothing Nothing
thierry = Sheep "Thierry" (Just eve) (Just adam)
edith = Sheep "Edith" (Just eve) (Just adam)
cecile = Sheep "Cecile" (Just edith) (Just thierry)
in Sheep "Dolly" (Just cecile) Nothing

main :: IO ()
main = let dolly = breedSheep
in do { print (mom dolly);
print (dad dolly);
print (maternalGrandfather dolly);
}

Just "Cecile"
Nothing
Just "Thierry"
haskell
maternalGrandfather :: Sheep -> Maybe Sheep
maternalGrandfather s = case (mom s) of
Nothing -> Nothing
Just m -> dad m

haskell
maternalGrandfather :: Sheep -> Maybe Sheep
maternalGrandfather s = case (mom s) of
Nothing -> Nothing
Just m -> dad m

greatGreatGreatGreatGreatGreat-
MaternalGrandfather ?
haskell
comb :: Maybe a -> (a -> Maybe b) -> Maybe b
comb Nothing _ = Nothing
comb (Just x) f = f x

maternalGrandfather :: Sheep -> Maybe Sheep


maternalGrandfather s = (Just s) `comb` mom `comb` dad

haskell
scala's Option type

data Option T = Some T | None

correspondence with Haskell. Idiom: methods that might return a null should return an Option[ T ]
scala's Option type

parametric polymorphism in scala => type param in square boxes. Option is abstract. Explain
isEmpty, isDefined. There are more methods in Option which are hidden.
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}

Some[ S ] <: Some[ T ]

S <: T

+T means it is covariant. Only has meaning for static type languages like java/scala. Covariance &
immutable types. Ruby: Float < Numeric; Integer < Numeric
scala
scala> var i:Option[Int] = Some(35)
i: Option[Int] = Some(35)

simple example in scala interpreter akin to Ruby's irb. Explain about type inference & how it differs
from Ruby. i.e. types in Ruby are not associated with the variables but literals. Once a scala variable
is inferred or declared to be of some type T; it cannot change. scala
scala> var i:Option[Int] = Some(35)
i: Option[Int] = Some(35)

scala> i
res0: Option[Int] = Some(35)

scala> i.get
res1: Int = 35

simple example in scala interpreter akin to Ruby's irb. Explain about type inference & how it differs
from Ruby. i.e. types in Ruby are not associated with the variables but literals. Once a scala variable
is inferred or declared to be of some type T; it cannot change. scala
var provider = Provider( 55 ) // strong

// thread
val runnable = new Runnable
{
val providerRef = new WeakReference( provider ) // weak

def getProvider():Option[Provider] =
{
val p = providerRef.get
if( p != null )
Some( p ) // boxing
else
None
}
...
def useProvider():Int
}
scala
Method #1: imperative way

def useProvider():Int =
{
val p = getProvider

if( p == None )
-1
else
p.value
}

scala
Method #2: Pattern matching

def useProvider():Int =
{
getProvider match
{
case None => -1
case Some(p) => p.value
}
}

scala
Higher Order Functions (HOFs)

scala> val list2 = 1 :: 2 :: 3 :: 4 :: Nil


list2: List[Int] = List(1, 2, 3, 4)

scala> list2.map( x => x * 2 )


res18: List[Int] = List(2, 4, 6, 8)

scala> list2 filter ( _ % 2 != 0 )


res28: List[Int] = List(1, 3)

scala
HOFs

Pattern match with Option[T] <---> HOFs version


var option:Option[Int] = ...

def double( i: Int ):Int = { i * 2 }

option match {
case None => None
case Some(x) => Some(double(x))
}

scala
var option:Option[Int] = ...

def double( i: Int ):Int = { i * 2 }

option match {
case None => None option.map( double(_) )
case Some(x) => Some(double(x))
}

scala
var option:Option[Int] = ...

def double( i: Int ):Int = { i * 2 }

option match {
case None => None option.map( double(_) )
case Some(x) => Some(double(x))
}

option match {
case None => foo option.getOrElse(foo)
case Some(x) => x
}

getOrElse not a HOF!


scala
Method #3: simplified

def useProvider():Int =
{
getProvider.getOrElse( -1 )
}

scala
For-Comprehensions
for{
val b <- books;
val a <- b.authors;
a.startsWith("E")
} yield b.title

rewritten as:

books.flatMap( b => b.authors


.filter( a => a.startsWith( "E" ) )
.map( c => b.title ) )

(defn) bridge b/w imperative forloops and FP's HOFs. Similar to haskell list comprehension
scala
Method #4: for comprehension

def useProvider():Int =
{
(for{
val p <- getProvider
} yield { p.value }
) getOrElse -1
}

scala
Safe method chaining

try(...) { |sir| sir.may.i.have.some.more }

for{
val r <- getSirFromSomeContext
val m <- r.may
val i <- m.i
val h <- i.have
val some <- h.some

} yield some.more
scala
Real world example (liftweb)
def confirmDelete
{
(for { val id <- param("id")
val user <- User.find(id))
} yield {
user.delete_!
notice("User deleted")
redirectTo("/simple/index.html")
}) getOrElse {
error("User not found");
redirectTo("/simple/index.html")}
}
}
scala with sails scala
Last words


1 heap allocation per Some [T]


Can [T] = Empty | Full [T] | Failure


data Either a b = Left a | Right b (haskell)


Either[ A, B ] destined for scala 2.7.1


nullable types in Scala ? Perhaps use the NotNull
trait for now.
Citations
Beust, Cedric. “A programming language for 2010.”. Blog. 24 mar 2006
http://snipurl.com/epicslides

Bong, Gavin. Sheep.hs source. Derived from Jeff Newbern's work.


http://pastie.caboo.se/170158

Braithwaite, Reginald. ick for Ruby. Homepage. version 0.2.3


http://ick.rubyforge.org/

Bonniot,Daniel. "Nice's Option Types". Wiki. 14 aug 2003.


http://snipurl.com/niceopt

Eng, Brian, and Jeff Cohen. "The mysterious Dr. Nil". 10 nov 2007.
http://snipurl.com/rubynil

Microsoft. "Using Nullable Types (C#). Reference. 2008


http://snipurl.com/csnull
Citations
Miller, Greg. “Messaging nil in Objective-C”. Blog. 11 feb 2006.
http://snipurl.com/objcnull

Morris, Tim. “scala.Option Cheat Sheet”. Blog. 16 jan 2008


http://snipurl.com/optcheat

Newbern, Jeff. “Meet the monads”. Homepage. version 1.1.0


http://snipurl.com/sheephas

Odersky, Martin. "Scala by example (draft)". PDF. 2008


http://snipurl.com/scaex

Pollak, David. “The Scala Option class and how lift uses it “. Blog. 13 apr 2007
http://snipurl.com/liftopt

Thinkgeek. “Man woman Tshirt”. Homepage.


http://thinkgeek.com/tshirts/itdepartment/5b7e/
the end

This work is released under the Creative Commons License.

http://raverun.com/license/2008/nullhell

You might also like