A Nil Device, A Lonely Operator, and a Voyage to the Void Star

Facce030b679bda34eb7c64885a741fc?s=47 Eric Weinstein
September 09, 2016

A Nil Device, A Lonely Operator, and a Voyage to the Void Star

Slides for my RubyKaigi 2016 talk on... nothing!

Facce030b679bda34eb7c64885a741fc?s=128

Eric Weinstein

September 09, 2016
Tweet

Transcript

  1. A Nil Device, a Lonely Operator, & a Voyage to

    the Void Star # Eric Weinstein # Ruby Kaigi 2016 # Kyoto, Japan # 9 September 2016
  2. for Joshua ジョシュアのための

  3. Part 0: Hello! こんにちは!

  4. About Me eric_weinstein = { # エリック・ワインスタイン employer: 'Hulu', github:

    'ericqweinstein', twitter: 'ericqweinstein', website: 'ericweinste.in' } 30% off with KAIGI30!
  5. Agenda • A Nil Device: What is nil? What is

    it for? • A Lonely Operator: &. in Ruby 2.3 (and how other languages handle their versions of nil) • A Voyage to the Void Star: A history of nothingness • Summary and takeaways • Questions!
  6. Part 1: A Nil Device

  7. None
  8. Questions? Thank You!

  9. Nil in C VALUE rb_cNilClass; // &c rb_cNilClass = rb_define_class("NilClass",

    rb_cObject); rb_define_method(rb_cNilClass, "to_i", nil_to_i, 0); rb_define_method(rb_cNilClass, "to_f", nil_to_f, 0); rb_define_method(rb_cNilClass, "to_s", nil_to_s, 0); // More of these, we’ll see them again soon!
  10. Nil in C (cont'd) special_singleton_class_of(VALUE obj) { SPECIAL_SINGLETON(Qnil, rb_cNilClass); SPECIAL_SINGLETON(Qfalse,

    rb_cFalseClass); SPECIAL_SINGLETON(Qtrue, rb_cTrueClass); return Qnil; }
  11. Nil as a Value #if USE_FLONUM RUBY_Qnil = 0x08, /*

    ...0000 1000 */ #else RUBY_Qnil = 4 // Then, later on... #define Qnil ((VALUE)RUBY_Qnil)
  12. MYSTERY SOLVED # 1.9.3 > nil.object_id => 4 # 2.3.0

    > nil.object_id => 8
  13. None
  14. Nil in Ruby > nil.class => NilClass > nil.singleton_class =>

    NilClass
  15. Nil in Ruby (cont'd) > NilClass.instance_methods(false).sort => [:&, :^, :inspect,

    :nil?, :rationalize, :to_a, : to_c, :to_f, :to_h, :to_i, :to_r, :to_s, :|]
  16. Nil as an API "The underlying problem is that Animal.find

    returns objects which conform to different APIs." — Sandi Metz http://www.sandimetz.com/blog/2014/12/19/ suspicions-of-nil
  17. Schrödinger’s Black Box • We could get something (a value)

    or nothing (nil) • We don't know what's in the box until we open it (!)
  18. Nil Means Something • No value • No meaningful value

    • Neutral value • Failure • Error
  19. Part 2: A Lonely Operator

  20. Some Common Sources of Nil my_hash[:imaginary] # => nil

  21. @oops @oops # => nil

  22. oops! f = 'FOO'.upcase! # => nil

  23. I/O File.size? 'pretend.txt' # => nil

  24. Nil in Ruby on Rails Model.find_by_* (Rails 3) and Model.find_by

    (Rails 4+)
  25. TL;DR Common theme: I/O and mutation can lead to nil

  26. &. The "lonely" or "safe" navigation operator: &.

  27. Why is Nil Dangerous? • What do we mean by

    "safe" and why is nil unsafe? • Should we protect ourselves from nil, or stop nil in the first place? Do we need nil at all? • If we don't handle nil, it infects everything
  28. KILL NIL

  29. How We Handle Nil Now • Active Support's Object#try •

    Null object pattern • &. • Whack-a-mole (are you nil? Are you nil?)
  30. Object#try @fox.try(:best_friend).try(:favorite_food) # => 'Chunky bacon' || nil

  31. Null Object class Fox attr_reader :foods, :name def initialize(name, foods)

    @name = name @foods = foods end end fox = Fox.new('Fox small', ['Chunky bacon']) fox.name # => "Fox small" class NullFox attr_reader :foods, :name def initialize @name = 'No name' @foods = [] end end faux = NullFox.new faux.name # => "No name"
  32. &. foxes = { fox_small: { mood: 'Nonplussed' }, fox_tall:

    { mood: 'Fussy', food: 'Chunky bacon' } } foxes[:fox_tall][:food]&.upcase # "CHUNKY BACON" foxes[:fox_small][:food]&.upcase # nil
  33. Comparative Literature • How other languages think about... well, nothing!

    • Clojure: nil and nil punning • Haskell: the case for Nothing • Java: Optionals (new in Java 8)
  34. Nil in Clojure (def foxes {:fox-small "small" :fox-tall {:mood "Fussy"

    :food "Chunky bacon"}}) (get-in foxes [:fox-tall :food]) ;; "Chunky bacon" (get-in foxes [:fox-small :food]) ;; nil (get-in foxes [:fox-medium :food]) ;; nil
  35. Nothing in Haskell import Prelude hiding (lookup) import Data.Map foxFoods

    = fromList([("Fox small", "chunky bacon"), ("Fox tall", "a single quail egg")]) foodMoods = fromList([("chunky bacon", "satisfied"), ("a single quail egg", "still hungry")])
  36. Nothing in Haskell (cont'd) foxMood :: String -> Maybe String

    foxMood name = do food <- lookup name foxFoods lookup food foodMoods
  37. Nothing in Haskell (cont'd) main = do putStrLn $ "Fox

    small " ++ (show (foxMood "Fox small")) putStrLn $ "Fox tall " ++ (show (foxMood "Fox tall")) putStrLn $ "Fox medium " ++ (show (foxMood "Fox medium")) -- Fox small Just "satisfied" -- Fox tall Just "still hungry" -- Fox medium Nothing
  38. &. Now for Something a Little Trickier • Nothing: no

    cached result for the search. • Just Nothing: we’ve done the search, but not found a record for the person. • Just (Just Nothing): we’ve done the search, and the result was: the database doesn't know person’s age! (Age is NULL) • Just (Just (Just 42)): we’ve found who we’re looking for and they’re 42 years old. • More @ http://bit.ly/2c5YSXQ (Luke Plant) and http://bit.ly/ 1gXXCEp (James Coglan)
  39. Null in Java private static final HashMap<String, String> FOXES =

    new HashMap(); public static void main(String[] args) { FOXES.put("Fox tall", "Tallish"); FOXES.put("Fox small", "Smallish"); System.out.println(FOXES.get("Fox tall").toUpperCase()); System.out.println(FOXES.get("Fox small").toUpperCase()); System.out.println(FOXES.get("Fox medium").toUpperCase()); // NPE! }
  40. The Ocho™ public static String safeGet(String k) { return Optional.ofNullable(FOXES.get(k)).orElseGet(()

    -> "No Value"); } public static void main(String[] args) { System.out.println(safeGet("Fox tall").toUpperCase()); System.out.println(safeGet("Fox small").toUpperCase()); System.out.println(safeGet("Fox medium").toUpperCase()); }
  41. Brought to You by the Letter “C” Speaking of C:

    why do we have nil/NULL at all?
  42. Part 3: A Voyage to the Void Star

  43. “I call it my billion-dollar mistake.” “It was the invention

    of the null reference in 1965... My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement.” — C.A.R. Hoare, 2009
  44. None
  45. A Little History • Invented by Tony Hoare in 1965

    for ALGOL W • ALGOL influenced C (-> MRI) & Java (-> JRuby) • Most, if not all, ALGOL-influenced languages have null pointers (e.g. C, C++, Java, JavaScript, Rust)
  46. A World Without Nil • Meaningless states wouldn't be representable!

    This is great... • ...but meaningless states show up all the time
  47. A World Without Null Pointers

  48. Summary • What is nil? What is it for? •

    Nil in Ruby (and ways to handle it) • Comparative literature • History of nil and NULL
  49. Takeaways (TL;DPA) • Nil is powerful, dangerous, and ubiquitous •

    We probably need nil, but we might not need null pointers! • Don't be a lonely operator! Ruby is a community—we're here to learn from each other • You can and should use the lonely operator (&.), but as with all tools, use it with care
  50. Thank You! ありがとうございました

  51. Questions? eric_weinstein = { # エリック・ワインスタイン employer: 'Hulu', github: 'ericqweinstein',

    twitter: 'ericqweinstein', website: 'ericweinste.in' } 30% off with KAIGI30!