Skip to content

Commit

Permalink
Added my old "Better Ruby through FP" talks.
Browse files Browse the repository at this point in the history
  • Loading branch information
chicagoscala committed Feb 1, 2012
1 parent d7d176a commit 9aee018
Show file tree
Hide file tree
Showing 24 changed files with 484 additions and 0 deletions.
Binary file added BetterRubyThroughFP/BetterRubyThroughFP.key
Binary file not shown.
Binary file added BetterRubyThroughFP/BetterRubyThroughFP.pdf
Binary file not shown.
Binary file added BetterRubyThroughFP/SonOfBetterRubyThroughFP.key
Binary file not shown.
Binary file added BetterRubyThroughFP/SonOfBetterRubyThroughFP.pdf
Binary file not shown.
56 changes: 56 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/actor-example.rb
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/age.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
class Age
 attr_reader :age
def initialize value
raise unless positive(value)
@age = value
end
def + amount
raise unless positive(amount)
Age.new (@age + amount)
end
private
def positive(x); x >= 0.0; end
end
16 changes: 16 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/case-class-example.rb
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/case-example.rb
Original file line number Diff line number Diff line change
@@ -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)
18 changes: 18 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/complex.rb
Original file line number Diff line number Diff line change
@@ -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
35 changes: 35 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/complex_spec.rb
Original file line number Diff line number Diff line change
@@ -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
13 changes: 13 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/currying.rb
Original file line number Diff line number Diff line change
@@ -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
22 changes: 22 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/fibonacci.rb
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/file-example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
File.new("file-example.rb").each_with_index do |line, n|
printf "%3d: %s", (n+1), line
end
30 changes: 30 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/first-class-funcs.rb
Original file line number Diff line number Diff line change
@@ -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"]

18 changes: 18 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/for-loop.rb
Original file line number Diff line number Diff line change
@@ -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"
42 changes: 42 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/frozen-person.rb
Original file line number Diff line number Diff line change
@@ -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"


19 changes: 19 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/functional-person.rb
Original file line number Diff line number Diff line change
@@ -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}

14 changes: 14 additions & 0 deletions BetterRubyThroughFP/fp-ruby-code-examples/immutable-person.rb
Original file line number Diff line number Diff line change
@@ -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=' ...

Loading

0 comments on commit 9aee018

Please sign in to comment.