Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Added my old "Better Ruby through FP" talks.

  • Loading branch information...
commit 9aee0186d481037f38e769b17ece5f47f6ad82da 1 parent d7d176a
chicagoscala chicagoscala authored

Showing 24 changed files with 484 additions and 0 deletions. Show diff stats Hide diff stats

  1. BIN  BetterRubyThroughFP/BetterRubyThroughFP.key
  2. BIN  BetterRubyThroughFP/BetterRubyThroughFP.pdf
  3. BIN  BetterRubyThroughFP/SonOfBetterRubyThroughFP.key
  4. BIN  BetterRubyThroughFP/SonOfBetterRubyThroughFP.pdf
  5. +56 0 BetterRubyThroughFP/fp-ruby-code-examples/actor-example.rb
  6. +12 0 BetterRubyThroughFP/fp-ruby-code-examples/age.rb
  7. +16 0 BetterRubyThroughFP/fp-ruby-code-examples/case-class-example.rb
  8. +35 0 BetterRubyThroughFP/fp-ruby-code-examples/case-example.rb
  9. +18 0 BetterRubyThroughFP/fp-ruby-code-examples/complex.rb
  10. +35 0 BetterRubyThroughFP/fp-ruby-code-examples/complex_spec.rb
  11. +13 0 BetterRubyThroughFP/fp-ruby-code-examples/currying.rb
  12. +22 0 BetterRubyThroughFP/fp-ruby-code-examples/fibonacci.rb
  13. +3 0  BetterRubyThroughFP/fp-ruby-code-examples/file-example.rb
  14. +30 0 BetterRubyThroughFP/fp-ruby-code-examples/first-class-funcs.rb
  15. +18 0 BetterRubyThroughFP/fp-ruby-code-examples/for-loop.rb
  16. +42 0 BetterRubyThroughFP/fp-ruby-code-examples/frozen-person.rb
  17. +19 0 BetterRubyThroughFP/fp-ruby-code-examples/functional-person.rb
  18. +14 0 BetterRubyThroughFP/fp-ruby-code-examples/immutable-person.rb
  19. +51 0 BetterRubyThroughFP/fp-ruby-code-examples/money-spec.rb
  20. +40 0 BetterRubyThroughFP/fp-ruby-code-examples/money.rb
  21. +18 0 BetterRubyThroughFP/fp-ruby-code-examples/mutable.rb
  22. +11 0 BetterRubyThroughFP/fp-ruby-code-examples/person-hash.rb
  23. +18 0 BetterRubyThroughFP/fp-ruby-code-examples/shapes.rb
  24. +13 0 BetterRubyThroughFP/fp-ruby-code-examples/side-effect-free.rb
BIN  BetterRubyThroughFP/BetterRubyThroughFP.key
Binary file not shown
BIN  BetterRubyThroughFP/BetterRubyThroughFP.pdf
Binary file not shown
BIN  BetterRubyThroughFP/SonOfBetterRubyThroughFP.key
Binary file not shown
BIN  BetterRubyThroughFP/SonOfBetterRubyThroughFP.pdf
Binary file not shown
56 BetterRubyThroughFP/fp-ruby-code-examples/actor-example.rb
... ... @@ -0,0 +1,56 @@
  1 +# Uses MenTaLguY's Concurrent Gem
  2 +# Runs on MRI (1.8)
  3 +# Example adapted from http://groups.google.com/group/ruby-talk-google/browse_thread/thread/ec442079705d168a
  4 +
  5 +require 'rubygems'
  6 +require 'concurrent/actors'
  7 +require 'case'
  8 +
  9 +require 'shapes'
  10 +
  11 +include Concurrent::Actors
  12 +
  13 +Message = Case::Struct.new :reply_to, :body
  14 +
  15 +def make_message body
  16 + # getting "mailbox" address of the current actor
  17 + current = Actor.current
  18 + Message.new current, body
  19 +end
  20 +
  21 +display = Actor.spawn do
  22 + loop do
  23 + # guarded receive (does case-like matching via #===)
  24 + Actor.receive do |msg|
  25 + msg.when Message[Object, Shape] do |m|
  26 + s = m.body.draw
  27 + m.reply_to << "drew: #{s}"
  28 + end
  29 + msg.when Message[Object, :exit] do |m|
  30 + m.reply_to << "exiting"
  31 + end
  32 + msg.when Message[Object, Object] do |m|
  33 + m.reply_to << "Error! #{m.body}"
  34 + end
  35 + end
  36 + end
  37 +end
  38 +
  39 +# sending shapes to display.
  40 +display << make_message(Rectangle.new)
  41 +display << make_message(Circle.new)
  42 +# sending other stuff...
  43 +display << make_message("Hello Display!")
  44 +display << make_message(:exit)
  45 +
  46 +loop do
  47 + Actor.receive do |msg|
  48 + msg.when "exiting" do
  49 + p "exiting..."
  50 + exit
  51 + end
  52 + msg.when String do |s|
  53 + p "reply: #{s}"
  54 + end
  55 + end
  56 +end
12 BetterRubyThroughFP/fp-ruby-code-examples/age.rb
... ... @@ -0,0 +1,12 @@
  1 +class Age
 attr_reader :age
  2 + def initialize value
  3 + raise … unless positive(value)
  4 + @age = value
  5 + end
  6 + def + amount
  7 + raise … unless positive(amount)
  8 + Age.new (@age + amount)
  9 + end
  10 + private
  11 + def positive(x); x >= 0.0; end
  12 +end
16 BetterRubyThroughFP/fp-ruby-code-examples/case-class-example.rb
... ... @@ -0,0 +1,16 @@
  1 +require 'rubygems'
  2 +require 'case'
  3 +require 'shapes'
  4 +
  5 +def example(arg)
  6 + case arg
  7 + when Shape
  8 + p arg.draw
  9 + when "exit"
  10 + p "exiting..."
  11 + end
  12 +end
  13 +
  14 +[Circle.new, Rectangle.new, "exit"].each do |t|
  15 + example(t)
  16 +end
35 BetterRubyThroughFP/fp-ruby-code-examples/case-example.rb
... ... @@ -0,0 +1,35 @@
  1 +# Adapted from MenTaLguY's InfoQ interview: http://www.infoq.com/articles/actors-rubinius-interview
  2 +require 'rubygems'
  3 +require 'case'
  4 +
  5 +Foo = Case::Struct.new :a, :b
  6 +
  7 +
  8 +def example(arg)
  9 + case arg
  10 + when Foo[:blarg, Object] # matches any Foo with :a == :blarg
  11 + p "matched a :blarg"
  12 + when Foo[10, 20] # matches only a Foo with .a == 10 and .b == 20
  13 + p "matched 10 & 20"
  14 + when Foo # matches any Foo
  15 + p "matched another Foo: #{arg}"
  16 + when Case[1, 2, Object] # matches a three-element array with initial elements 1, 2:
  17 + p "matched [1, 2, Object], where the Object is #{arg[2].inspect}"
  18 + when Case::Any[String, Array] # matches either a String or Array (Note: after previous case!)
  19 + p "matched a String or Array: #{arg.inspect}"
  20 + when Case::All[Integer, Case.guard { |n| n > 10 }] # matches any Integer > 10:
  21 + p "matched an integer > 10"
  22 + else
  23 + p "matched none of the above! \"#{arg}\""
  24 + end
  25 +end
  26 +
  27 +example(Foo.new(:blarg, :blech))
  28 +example(Foo.new(10, 20))
  29 +example(Foo.new(:aaa, :bbb))
  30 +example([1, 2, "boo!"])
  31 +example([3, 4, "boo!"])
  32 +example("Hello")
  33 +example([:c, :d, :e, :f])
  34 +example(11)
  35 +example(10)
18 BetterRubyThroughFP/fp-ruby-code-examples/complex.rb
... ... @@ -0,0 +1,18 @@
  1 +module Math
  2 + class Complex
  3 + attrib_reader :real, :imag
  4 +
  5 + def initialize real, imag
  6 + @real = real
  7 + @imag = imag
  8 + end
  9 +
  10 + def + other
  11 + Complex.new(real + other.real, imag + other.imag)
  12 + end
  13 +
  14 + def - other
  15 + Complex.new(real - other.real, imag - other.imag)
  16 + end
  17 + end
  18 +end
35 BetterRubyThroughFP/fp-ruby-code-examples/complex_spec.rb
... ... @@ -0,0 +1,35 @@
  1 +require 'rubygems'
  2 +require 'spec'
  3 +require 'Complex'
  4 +
  5 +module Math
  6 +
  7 + describe "Complex" do
  8 + before :all do
  9 + @samples = (-3..3).inject([]) do |list, i|
  10 + list += (-3..3).inject([]) do |list, j|
  11 + list << Complex.new(1.1 * i, 1.1 * j)
  12 + end
  13 + end
  14 + end
  15 +
  16 + it "should support addition where a new value is returned that sums the input real and imaginary values" do
  17 + @samples.each do |c1|
  18 + @samples.each do |c2|
  19 + c12 = c1 + c2
  20 + c12.real.should == c1.real + c2.real
  21 + c12.imag.should == c1.imag + c2.imag
  22 + end
  23 + end
  24 + end
  25 + it "should support subtraction where a new value is returned that subtracts the input real and imaginary values" do
  26 + @samples.each do |c1|
  27 + @samples.each do |c2|
  28 + c12 = c1 - c2
  29 + c12.real.should == c1.real - c2.real
  30 + c12.imag.should == c1.imag - c2.imag
  31 + end
  32 + end
  33 + end
  34 + end
  35 +end
13 BetterRubyThroughFP/fp-ruby-code-examples/currying.rb
... ... @@ -0,0 +1,13 @@
  1 +mod = lambda {|m, n| n % m}
  2 +p mod.(2,5) # ==> 1
  3 +
  4 +modcurry = mod.curry
  5 +mod2 = modcurry.(2)
  6 +p mod2.(5) # ==> 1
  7 +
  8 +mod3 = modcurry.(3)
  9 +p mod3.(5) # ==> 2
  10 +p mod3.class
  11 +
  12 +p mod35 = modcurry.(3,5) # ==> 2
  13 +p mod35.class
22 BetterRubyThroughFP/fp-ruby-code-examples/fibonacci.rb
... ... @@ -0,0 +1,22 @@
  1 +# Recursive definition of the Fibonacci sequence.
  2 +# f(n) = 0 for n == 0
  3 +# = 1 for n == 1
  4 +# = f(n-1) + f(n-2) for n > 1
  5 +require 'rubygems'
  6 +require 'spec'
  7 +
  8 +describe "Fibonacci sequence" do
  9 + def fib n
  10 + case n
  11 + when 0..1 then n
  12 + else fib(n-1) + fib(n-2)
  13 + end
  14 + end
  15 +
  16 + it "should return the correct result for an input integer >= 0" do
  17 + expected = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
  18 + (0...expected.length).each do |i|
  19 + fib(i).should == expected[i]
  20 + end
  21 + end
  22 +end
3  BetterRubyThroughFP/fp-ruby-code-examples/file-example.rb
... ... @@ -0,0 +1,3 @@
  1 +File.new("file-example.rb").each_with_index do |line, n|
  2 + printf "%3d: %s", (n+1), line
  3 +end
30 BetterRubyThroughFP/fp-ruby-code-examples/first-class-funcs.rb
... ... @@ -0,0 +1,30 @@
  1 +def a_first? s
  2 + s[0] == ?a
  3 +end
  4 +
  5 +def filter array
  6 + array.select {|s| yield s}
  7 +end
  8 +
  9 +array = ["aa", "ab", "bb", "ba", "bc", "ac"]
  10 +# The following won’t "compile":
  11 +# array2 = filter array, a_first?
  12 +# array2 = filter array, &a_first?
  13 +
  14 +array2 = filter(array) {|s| a_first? s}
  15 +p array2 # => ["aa", "ab", "ac"]
  16 +
  17 +array3 = filter array, &method(:a_first?)
  18 +p array3 # => ["aa", "ab", "ac"]
  19 +
  20 +def filter2 array, test
  21 + array.select &test
  22 +end
  23 +
  24 +# Both of the following 2 defs work
  25 +pred = self.method(:a_first?).to_proc
  26 +pred = Proc.new {|s| a_first? s}
  27 +array4 = filter2 array, pred
  28 +
  29 +p array4 # => ["aa", "ab", "ac"]
  30 +
18 BetterRubyThroughFP/fp-ruby-code-examples/for-loop.rb
... ... @@ -0,0 +1,18 @@
  1 +for i in [1, 2, 3, 4, 5]
  2 + print "#{i}, "
  3 +end
  4 +print "\n"
  5 +
  6 +hash = {:a => 'a', :b => 'b', :c => 'c'}
  7 +hash.each do |k, v|
  8 + puts "#{k} => #{v}"
  9 +end
  10 +
  11 +def print_array array, i = 0
  12 + return if i == array.length
  13 + print "#{array[i]}, "
  14 + print_array array, i+1
  15 +end
  16 +
  17 +print_array [1, 2, 3, 4, 5]
  18 +print "\n"
42 BetterRubyThroughFP/fp-ruby-code-examples/frozen-person.rb
... ... @@ -0,0 +1,42 @@
  1 +class Person
  2 + attr_accessor :first_name, :last_name, :age, :addresses # etc.
  3 +
  4 + def initialize first_name, last_name, age, addresses
  5 + @first_name = first_name
  6 + @last_name = last_name
  7 + @age = age
  8 + @addresses = addresses.clone
  9 + end
  10 +
  11 + def freeze
  12 + super
  13 + @addresses.freeze
  14 + end
  15 +
  16 + def to_s
  17 + "#{@first_name} #{@last_name}, age #{@age}, at #{@addresses.inspect}"
  18 + end
  19 +end
  20 +
  21 +dean = Person.new "Dean", "Wampler", 29, ["1st St.", "2nd St.", "3rd St."]
  22 +p dean.first_name # => "Dean"
  23 +dean.first_name = "Bubba"
  24 +p dean.first_name # => "Bubba"
  25 +dean.freeze
  26 +begin
  27 +dean.first_name = "Joe" # => frozen-person.rb:16:in `first_name=': can't modify frozen object (TypeError)
  28 + # from frozen-person.rb:16
  29 +rescue
  30 +end
  31 +begin
  32 +dean.addresses[0] = "Infinity Loop" # => frozen-person.rb:16:in `first_name=': can't modify frozen object (TypeError)
  33 + # from frozen-person.rb:16
  34 +rescue
  35 + p "Addresses not changed: #{dean.addresses}"
  36 +end
  37 +p "dean: #{dean}"
  38 +# But:
  39 +dean = Person.new "Joe", "de Plumber", 40, ["Wisteria Lane"]
  40 +p dean.first_name # => "Joe"
  41 +
  42 +
19 BetterRubyThroughFP/fp-ruby-code-examples/functional-person.rb
... ... @@ -0,0 +1,19 @@
  1 +class Person
  2 + attr_reader :addresses
  3 +
  4 + def initialize *addresses
  5 + @addresses = *addresses
  6 + @addresses.clone.freeze
  7 + end
  8 +end
  9 +
  10 +def validate address
  11 + true # some operation...
  12 +end
  13 +
  14 +dean = Person.new "1 Memory Lane", "1 Hope Drive", "1 Infinite Loop"
  15 +dean.addresses.each {|a| validate(a) or raise "..."}
  16 +dean.addresses.each {|a| puts a}
  17 +dean.addresses.sort {|a1,a2| a1 <=> a2}.each {|a| puts a}
  18 +dean.addresses.sort! {|a1,a2| a1 <=> a2}.each {|a| puts a}
  19 +
14 BetterRubyThroughFP/fp-ruby-code-examples/immutable-person.rb
... ... @@ -0,0 +1,14 @@
  1 +class Person
  2 + attr_reader :first_name, :last_name, :age # etc.
  3 +
  4 + def initialize first_name, last_name, age
  5 + @first_name = first_name
  6 + @last_name = last_name
  7 + @age = age
  8 + end
  9 +end
  10 +
  11 +dean = Person.new "Dean", "Wampler", 29
  12 +p dean.first_name # => "Dean"
  13 +dean.first_name = "Bubba" # => ... undefined method `first_name=' ...
  14 +
51 BetterRubyThroughFP/fp-ruby-code-examples/money-spec.rb
... ... @@ -0,0 +1,51 @@
  1 +require 'rubygems'
  2 +require 'spec'
  3 +require 'Money'
  4 +
  5 +def value money
  6 + money.send(:value)
  7 +end
  8 +
  9 +describe "Money" do
  10 + before :all do
  11 + @samples = (-3..3).inject([]) do |list, i|
  12 + list << Money.new(1.1 * i)
  13 + end
  14 + end
  15 +
  16 + it "should support addition" do
  17 + @samples.each do |m1|
  18 + @samples.each do |m2|
  19 + m12 = m1 + m2
  20 + value(m12).should == value(m1) + value(m2)
  21 + end
  22 + end
  23 + end
  24 + it "should support subtraction" do
  25 + @samples.each do |m1|
  26 + @samples.each do |m2|
  27 + m12 = m1 - m2
  28 + value(m12).should == value(m1) - value(m2)
  29 + end
  30 + end
  31 + end
  32 +end
  33 +
  34 +describe "Dollar" do
  35 + before :all do
  36 + @samples = (-3..3).inject([]) do |list, i|
  37 + list << Dollar.new(1.1111 * i)
  38 + end
  39 + end
  40 +
  41 + it "should print in the form $MMMMM.NN" do
  42 + @samples.each do |m|
  43 + v = value m
  44 + int = v.to_i
  45 + fraction = format("%.2d", ((v - int) * 100).to_i.abs)
  46 + m.to_s.should == "$#{int}.#{fraction}"
  47 + end
  48 + end
  49 +end
  50 +
  51 +
40 BetterRubyThroughFP/fp-ruby-code-examples/money.rb
... ... @@ -0,0 +1,40 @@
  1 +class Money
  2 + def initialize v
  3 + @value = v
  4 + end
  5 +
  6 + def + other
  7 + # TODO: Handle rounding properly...
  8 + Money.new(value + other.value)
  9 + end
  10 +
  11 + def - other
  12 + # TODO: Handle rounding properly...
  13 + Money.new(value - other.value)
  14 + end
  15 +
  16 + def * factor
  17 + # TODO: Handle rounding properly...
  18 + Money.new(value * factor)
  19 + end
  20 +
  21 + include Comparable
  22 +
  23 + def <=> other
  24 + # TODO: Handle rounding properly...
  25 + value <=> other.value
  26 + end
  27 +
  28 + protected
  29 + attr_reader :value # bad idea?
  30 +end
  31 +
  32 +class Dollar < Money
  33 + def initialize v
  34 + super v
  35 + end
  36 +
  37 + def to_s
  38 + format("$%.2f", value)
  39 + end
  40 +end
18 BetterRubyThroughFP/fp-ruby-code-examples/mutable.rb
... ... @@ -0,0 +1,18 @@
  1 +require 'rubygems'
  2 +require 'spec'
  3 +
  4 +describe "Mutable data hazards" do
  5 + class MutableName
  6 + attr_accessor :name
  7 + def initialize name
  8 + @name = name
  9 + end
  10 + end
  11 +
  12 + it "will change an object through an alias" do
  13 + m1 = MutableName.new "m1"
  14 + m2 = m1
  15 + m2.name = "m2"
  16 + m1.name.should == "m2"
  17 + end
  18 +end
11 BetterRubyThroughFP/fp-ruby-code-examples/person-hash.rb
... ... @@ -0,0 +1,11 @@
  1 +dean = {
  2 + :first_name => "Buck",
  3 + :last_name => "Trends",
  4 + :addresses => [
  5 + "1 Memory Lane",
  6 + "1 Hope Drive",
  7 + "1 Infinite Loop"]}
  8 +
  9 +dean[:addresses].each do |addr|
  10 + puts "Address: #{addr}"
  11 +end
18 BetterRubyThroughFP/fp-ruby-code-examples/shapes.rb
... ... @@ -0,0 +1,18 @@
  1 +class Shape
  2 + # "abstract" draw method.
  3 + # Sends commands to graphics engine
  4 +end
  5 +
  6 +class Rectangle < Shape
  7 + def draw
  8 + "Rectangle"
  9 + end
  10 +end
  11 +
  12 +class Circle < Shape
  13 + def draw
  14 + "Circle"
  15 + end
  16 +end
  17 +
  18 +# etc.
13 BetterRubyThroughFP/fp-ruby-code-examples/side-effect-free.rb
... ... @@ -0,0 +1,13 @@
  1 +# Yes
  2 +def filter array
  3 + array.select {|s| yield s}
  4 +end
  5 +
  6 +class Person
  7 + # No
  8 + attr_writer :first_name, :last_name, :age # etc.
  9 + ...
  10 +end
  11 +
  12 +# No
  13 +puts "Some string"

0 comments on commit 9aee018

Please sign in to comment.
Something went wrong with that request. Please try again.