Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added my old "Better Ruby through FP" talks.

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