Professional Documents
Culture Documents
Nullhell XMP
Nullhell XMP
null hell
Gavin Bong
gavin.emploi@gmail.com
http://raverun.com/license/2008/nullhell
motivation
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++
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
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
annotate
?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() );
System.Nullable<int> age = ..
if (age.HasValue)
{
System.Console.WriteLine(age.Value);
}
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
haskell
scala's Option type
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
}
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
HOFs
option match {
case None => None
case Some(x) => Some(double(x))
}
scala
var option:Option[Int] = ...
option match {
case None => None option.map( double(_) )
case Some(x) => Some(double(x))
}
scala
var option:Option[Int] = ...
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
}
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:
(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
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
Eng, Brian, and Jeff Cohen. "The mysterious Dr. Nil". 10 nov 2007.
http://snipurl.com/rubynil
Pollak, David. “The Scala Option class and how lift uses it “. Blog. 13 apr 2007
http://snipurl.com/liftopt
http://raverun.com/license/2008/nullhell