The Curious Incident of the Shadow in the Run-Time

The Curious Incident of the Shadow in the Run-Time

A presentation about the perils of shadowing in Ruby. It is an adaptation of my blog post The Curious Incident of the Shadow in the Run-Time (https://paulfioravanti.com/blog/2018/08/20/the-curious-incident-of-the-shadow-in-the-run-time/)

Presented at:

- Ruby on Rails Oceania Sydney meetup on 9 July 2019.
- Melbourne Ruby meetup on 27 May 2020 (via Zoom)

Presentation slide deck markdown and speaker notes (useable in Deckset 2):
https://github.com/paulfioravanti/presentations/tree/master/the_curious_incident_of_the_shadow_in_the_run_time

Abda861707b1e78e0fce47ced55f84ee?s=128

Paul Fioravanti

May 27, 2020
Tweet

Transcript

  1. 2.
  2. 3.
  3. 4.

    The

  4. 9.
  5. 11.
  6. 14.

    shadow.rb x = 42 3.times { |x| puts "x is

    #{x}" } $ ruby shadow.rb x is 0 x is 1 x is 2
  7. 15.

    shadow.rb x = 42 3.times { |x| puts "x is

    #{x}" } $ ruby shadow.rb x is 42 x is 42 x is 42
  8. 16.

    shadow.rb x = 42 3.times { |x| puts "x is

    #{x}" } $ ruby shadow.rb x is 0 x is 1 x is 2
  9. 19.

    shadow.rb x = 42 3.times { |x| puts "x is

    #{x}" } $ ruby -w shadow.rb
  10. 20.

    shadow.rb x = 42 3.times { |x| puts "x is

    #{x}" } $ ruby -w shadow.rb shadow.rb:2: warning: shadowing outer local variable - x shadow.rb:1: warning: assigned but unused variable - x x is 0 x is 1 x is 2
  11. 21.
  12. 22.
  13. 25.

    shadow.rb x = 42 3.times do |x| puts( "Local x

    is #{x} and "\ "outer x is #{'What goes here??'}" ) end
  14. 26.
  15. 27.

    shadow.rb y = 42 3.times { |x| puts "x is

    #{x} and y is #{y}" } $ ruby -w shadow.rb x is 0 and y is 42 x is 1 and y is 42 x is 2 and y is 42
  16. 28.

    "[Matz] thinks that shadowing by itself is a bad habit

    and should be discouraged." — Shouhei Urabe, quo/ng Matz
  17. 30.
  18. 31.
  19. 34.

    "If you have not assigned to one of these ambiguous

    names, Ruby will assume you wish to call a method."
  20. 35.

    "Once you have assigned to the name, Ruby will assume

    you wish to reference a local variable."
  21. 36.

    "The local variable is created when the parser encounters the

    assignment, not when the assignment occurs." — Local Variables and Methods Assignment sec9on of Ruby's syntax documenta9on
  22. 40.
  23. 41.
  24. 45.

    class Person def name @name end def name=(new_name) @name =

    new_name end def initialize(name = nil) @name = name end end
  25. 47.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  26. 48.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  27. 49.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  28. 50.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  29. 51.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  30. 52.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  31. 53.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  32. 54.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  33. 58.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  34. 59.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  35. 60.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  36. 61.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  37. 64.

    $ irb irb(main):001:0> require "./person.rb" true irb(main):002:0> Person.new("Matz").say_name My name

    is nil nil irb(main):003:0> Person.new.say_name My name is "Unknown" nil
  38. 65.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  39. 66.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  40. 67.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  41. 68.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  42. 69.

    class Person attr_accessor :name def initialize(name = nil) @name =

    name end def say_name if name.nil? name = "Unknown" end puts "My name is #{name.inspect}" end end
  43. 70.
  44. 71.
  45. 73.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: if name.nil? 14: name = "Unknown" 15: end 16: 17: puts "My name is #{name.inspect}" 18: end [1] pry(#<Person>)>
  46. 74.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: if name.nil? 14: name = "Unknown" 15: end 16: 17: puts "My name is #{name.inspect}" 18: end [1] pry(#<Person>)> name nil [2] pry(#<Person>)>
  47. 76.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: if name.nil? 14: name = "Unknown" 15: end 16: 17: puts "My name is #{name.inspect}" 18: end [1] pry(#<Person>)> name nil [2] pry(#<Person>)>
  48. 78.

    [1] pry(#<Person>)> name nil [2] pry(#<Person>)> next From: /person.rb @

    line 17 Person#say_name: 10: def say_name 11: binding.pry 12: 13: if name.nil? 14: name = "Unknown" 15: end 16: => 17: puts "My name is #{name.inspect}" 18: end [2] pry(#<Person>)>
  49. 79.

    [2] pry(#<Person>)> next From: /person.rb @ line 17 Person#say_name: 10:

    def say_name 11: binding.pry 12: 13: if name.nil? 14: name = "Unknown" 15: end 16: => 17: puts "My name is #{name.inspect}" 18: end [2] pry(#<Person>)> name nil [3] pry(#<Person>)> exit My name is nil
  50. 80.
  51. 84.
  52. 85.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts name.inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end [1] pry(#<Person>)>
  53. 86.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts name.inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end [1] pry(#<Person>)> name nil
  54. 87.

    [1] pry(#<Person>)> name nil [2] pry(#<Person>)> next "Matz" From: /person.rb

    @ line 14 Person#say_name: 10: def say_name 11: binding.pry 12: 13: puts name.inspect => 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  55. 88.
  56. 89.
  57. 90.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end [1] pry(#<Person>)>
  58. 91.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end [1] pry(#<Person>)> next "method"
  59. 92.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end [1] pry(#<Person>)> defined?(name) "local-variable"
  60. 93.
  61. 94.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  62. 95.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  63. 96.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  64. 97.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  65. 98.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  66. 99.

    irb(main):002:0> Person.new("Matz").say_name From: /person.rb @ line 13 Person#say_name: 10: def

    say_name 11: binding.pry 12: => 13: puts defined?(name).inspect 14: if name.nil? 15: name = "Unknown" 16: end 17: 18: puts "My name is #{name.inspect}" 19: end
  67. 100.
  68. 101.
  69. 106.

    def say_name name = self.name || "Unknown" puts "My name

    is #{name.inspect}" end def say_name name = name() || "Unknown" puts "My name is #{name.inspect}" end
  70. 107.