$30 off During Our Annual Pro Sale. View Details »

Ruby: A Family History

Ruby: A Family History

Ruby blends some of the best ideas from older programming languages including Smalltalk, Perl, Lisp, and Ada. In this talk, we’ll take a tour of these languages and their influences on Ruby, to better understand Ruby’s core ideas and how we can use it to its full potential.

Geoffrey Litt

December 12, 2017
Tweet

More Decks by Geoffrey Litt

Other Decks in Programming

Transcript

  1. Ruby:
    A Family History

    View Slide

  2. Why learn many
    programming languages?

    View Slide

  3. By learning other
    languages, we can
    broaden our horizons and
    also more deeply
    understand Ruby.
    — matz

    View Slide

  4. View Slide

  5. LISP

    View Slide

  6. Ruby was a Lisp
    originally, in
    theory.
    Let's call it
    MatzLisp from
    now on. ;-)
    — Matz

    View Slide

  7. View Slide

  8. Expressions
    ()
    (foo bar)
    (1 (2 3) (4 5))

    View Slide

  9. 7 primitive operators
    —quote
    —atom
    —eq
    —car
    —cdr
    —cons
    —cond

    View Slide

  10. quote
    (quote foo) ;=> foo
    'foo ;=> foo

    View Slide

  11. Lisp primitives: atom
    (atom 'foo) ;=> t
    (atom '(a b c)) ;=> ()

    View Slide

  12. Lisp primitives: eq
    (eq 'foo 'foo) ;=> t
    (eq 'foo 'bar) ;=> ()

    View Slide

  13. Why we need quote
    (atom (eq 'foo 'foo)) ;=> t
    (atom '(eq 'foo 'foo)) ;=> ()

    View Slide

  14. Lisp primitives: car
    (car '(foo bar baz)) ;=> foo

    View Slide

  15. Lisp primitives: cdr
    (cdr '(foo bar baz)) ;=> (bar baz)

    View Slide

  16. Lisp primitives: cons
    (cons 'foo '(bar baz)) ;=> (foo bar baz)

    View Slide

  17. Lisp primitives: cond
    (cond ((eq 'foo 'bar) 'first
    ((eq 'foo 'foo) 'second)) ;=> second

    View Slide

  18. Defining functions
    (lambda (arg1) (eq arg1 'foo))

    View Slide

  19. 7 primitive operators + function definition
    —quote
    —atom
    —eq
    —car
    —cdr
    —cons
    —cond
    —lambda

    View Slide

  20. An interpreter
    (defun eval (e a)
    (cond
    ((atom e) (assoc. e a))
    ((atom (car e))
    (cond
    ((eq (car e) 'quote) (cadr e))
    ((eq (car e) 'atom) (atom (eval (cadr e) a)))
    ((eq (car e) 'eq) (eq (eval (cadr e) a)
    (eval (caddr e) a)))
    ((eq (car e) 'car) (car (eval (cadr e) a)))
    ((eq (car e) 'cdr) (cdr (eval (cadr e) a)))
    ((eq (car e) 'cons) (cons (eval (cadr e) a)
    (eval (caddr e) a)))
    ((eq (car e) 'cond) (evcon. (cdr e) a))
    ;...

    View Slide

  21. View Slide

  22. View Slide

  23. View Slide

  24. FORTRAN syntax
    a = 12.0
    b = 15.0
    result = a + b
    print *, 'The total is ', result

    View Slide

  25. The project...was neither
    finalized nor explicitly
    abandoned. It just
    receded into the indefinite
    future...
    — John McCarthy

    View Slide

  26. Lisp firsts
    —conditionals
    —dynamic typing
    —garbage collection
    —symbol types

    View Slide

  27. Functions in Lisp
    (lambda (x) (* x 2))

    View Slide

  28. Functions in Lisp
    (map
    (lambda (x) (* x 2))
    (1 2 3))

    View Slide

  29. Functions in Lisp
    (map
    (lambda (x) (* x 2))
    (1 2 3))
    [1, 2, 3].map(
    &lambda { |x| x * 2 }
    )

    View Slide

  30. Functions in Ruby
    my_fun = lambda { |x| x * 2 } # => Proc
    def takes_a_lambda(fun)
    fun.call(2)
    end
    takes_a_lambda(my_fun) # => 4

    View Slide

  31. Functions in Ruby
    { |x| x * 2 } # => SyntaxError
    def takes_a_block
    yield 2
    end
    takes_a_block { |x| x * 2 } # => 4

    View Slide

  32. Concise lambdas
    (map (1 2 3) (lambda (x) (* x 2)))
    [1, 2, 3].map { |x| x * 2 }

    View Slide

  33. Concise lambdas
    (remove-if (lambda (n) (< n 4))
    (map (lambda (x) (* x 2))
    (1 2 3)))
    [1, 2, 3].
    map { |x| x * 2 }.
    reject{ |n| n < 4 }

    View Slide

  34. Concise lambdas
    (describe "my machine" (lambda () (
    (it "produces widgets" (lambda () (
    ;...
    ))))))
    describe "my machine" do
    it "produces widgets" do
    #...
    end
    end

    View Slide

  35. Multiple function arguments
    (even-odd-map
    (lambda (x) (* x 2))
    (lambda (x) (* x 3))
    (1 2 3 4))
    [1, 2, 3, 4].even_odd_map(
    lambda { |x| x * 2 },
    lambda { |x| x * 3 },
    )

    View Slide

  36. Lisp is consistent
    Ruby is pragmatic

    View Slide

  37. Concise lambdas catching on
    Ruby 1.9+:
    lambda { |x| x * 2 }
    ->(x) { x * 2 }

    View Slide

  38. Concise lambdas catching on
    Javascipt ES6:
    array.map(function(x) { x * 2 })
    array.map(x => x * 2)

    View Slide

  39. Concise lambdas catching on
    Scala:
    array.map(_ * 2)
    array.map(2*)

    View Slide

  40. Metaprogramming

    View Slide

  41. Macros
    #define square(x) x*x

    View Slide

  42. Macros
    #define square(x) x*x
    2/square(10)
    2/10*10

    View Slide

  43. Macros
    #define square(x) ( (x) * (x) )

    View Slide

  44. Macros
    (+ 1 2)

    View Slide

  45. Macros
    (defmacro (backwards . body)
    (cons 'begin
    (reverse body)))

    View Slide

  46. Macros
    (defmacro (backwards . body)
    (cons 'begin
    (reverse body)))
    # Fake Ruby macro syntax
    defmacro backwards(code)
    code.reverse
    end

    View Slide

  47. Ruby parsing/unparsing
    code = "2 + 3 * 4"
    ast = Parser::CurrentRuby.parse(code)

    View Slide

  48. Ruby parsing/unparsing
    code = "2 + 3 * 4"
    ast = Parser::CurrentRuby.parse(code)
    # => [s(:send,
    # s(:int, 2), :+,
    # s(:send,
    # s(:int, 3), :*,
    # s(:int, 4))), []]

    View Slide

  49. Ruby parsing/unparsing
    code = "2 + 3 * 4"
    ast = Parser::CurrentRuby.parse(code)
    # => [s(:send,
    # s(:int, 2), :+,
    # s(:send,
    # s(:int, 3), :*,
    # s(:int, 4))), []]
    2.1.5 :005 > Unparser.unparse(ast)
    # => "2 + (3 * 4)"

    View Slide

  50. Ruby metaprogramming
    define_method
    instance_eval
    class_eval
    method_missing

    View Slide

  51. Smalltalk

    View Slide

  52. What is "object-oriented programming"?
    inheritance
    polymorphism
    UML diagrams
    class hierarchies
    factories

    View Slide

  53. View Slide

  54. I could hardly believe how
    beautiful and wonderful
    the idea of LISP was...but
    there were deep flaws in
    its logical foundations.
    — Alan Kay

    View Slide

  55. View Slide

  56. View Slide

  57. View Slide

  58. In computer terms,
    Smalltalk is a recursion on
    the notion of computer
    itself.
    — Alan Kay

    View Slide

  59. Message passing
    3 + 4
    "=> 7

    View Slide

  60. View Slide

  61. View Slide

  62. View Slide

  63. Message passing similarities
    Smalltalk:
    3 perform: '+' asSymbol with: 4
    Ruby:
    3.send(:+, 4)

    View Slide

  64. Message passing similarities
    Smalltalk:
    3 isKindOf: Integer
    Ruby:
    3.is_a? Integer

    View Slide

  65. Message passing similarities
    Smalltalk:
    Integer isKindOf: Class
    Ruby:
    Integer.is_a? Class

    View Slide

  66. Message passing similarities
    Integer extend [
    doesNotUnderstand: msg [
    'method not defined' printNl
    ]
    ]
    class Integer
    def method_missing(msg)
    puts 'method not defined'
    end
    end

    View Slide

  67. Control flow with message sending
    Smalltalk:
    array do: [ :element | Transcript show: element ]
    Ruby:
    array.each { |element| puts element }

    View Slide

  68. Message passing purity
    (2 + 2 == 5)
    ifTrue: [ Transcript show: 'true'. ]
    ifFalse: [ Transcript show: 'false'. ].

    View Slide

  69. View Slide

  70. View Slide

  71. View Slide

  72. A brief aside: Other OO languages
    Not many people know this, but before I created
    Ruby, as a student I was an advocate for statically
    typed OO langauges.
    — Matz

    View Slide

  73. A brief aside: Other OO languages
    —Ada: 1977-83
    —Eiffel: 1985
    Important features
    —Multiple inheritance
    —Generics

    View Slide

  74. View Slide

  75. "A program is correct if it gets the job done before
    you get fired."
    -- Larry Wall

    View Slide

  76. Variable sigils
    $scalar
    @array
    %hash
    &subroutine
    *typeglob

    View Slide

  77. The easiest string interpolation
    $x = 5;
    $msg = "The value is $x now.";

    View Slide

  78. Weak typing
    print "8" + "1";

    View Slide

  79. Weak typing
    print "8" + "1"; # => 9

    View Slide

  80. Weak typing
    @cities = qw( Berlin Tokyo London Boston );
    # Assign the array to a scalar
    $cities_count = @cities;

    View Slide

  81. Weak typing
    @cities = qw( Berlin Tokyo London Boston );
    # Assign the array to a scalar
    $cities_count = @cities;
    # => 4

    View Slide

  82. Little Perl niceties
    —native regex syntax
    —%w array syntax
    —heredocs
    —1_000_000 number syntax

    View Slide

  83. Perl-inspired oddities
    $global_variable = 1

    View Slide

  84. Perl-inspired oddities
    $$

    View Slide

  85. Perl-inspired oddities
    $$ # => 19936
    Process ID!

    View Slide

  86. Perl-inspired oddities
    $/

    View Slide

  87. Perl-inspired oddities
    $/ # => "\n"
    Input record separator, obviously

    View Slide

  88. Perl-inspired oddities
    /([a-zA-Z]*)/ =~ string
    match_contents = $1

    View Slide

  89. Perl-inspired oddities
    END {
    puts "RB!"
    }
    puts "Boston"
    BEGIN {
    puts "Hello"
    }

    View Slide

  90. Perl-inspired oddities
    END {
    puts "RB!"
    }
    puts "Boston"
    BEGIN {
    puts "Hello"
    }
    # Hello
    # Boston
    # RB!

    View Slide

  91. Perl mantras
    "There's more than one way to do it"

    View Slide

  92. Perl mantras
    "Easy things should be easy and hard things should
    be possible"

    View Slide

  93. What is Ruby?
    Two elegant theoretical foundations + a big dose of
    practicality

    View Slide

  94. Thanks!
    @geoffreylitt

    View Slide