Tuesday, January 1, 2008

Pipelines Using Fibers in Ruby 1.9—Part II

In the previous post, I developed a class called PipelineElement. This made it relatively easy to create elements that act as producers and filters in a programmatic pipeline. Using it, we could write Ruby 1.9 code like:


    10.times do
puts (evens | multiples_of_three | multiples_of_seven).resume
end

The construct in the loop is a pipeline containing three chunks of code: a generator of even numbers, a filter that only passes multiples of three, and another filter that passes multiples of seven. Numbers are passed from the producer to the first filter, and then from that filter to the next, until finally popping out and being made available to puts.


However, creating these pipeline elements is still something of a pain. It turns out that we can simplify things when it comes to creating filters. In the implementation I’ll show here, we’ll only handle the case of simple transforming filters—filters that take an input, do something to it, and write the result to the filter chain.


Let’s revisit the PipelineElement class


    class PipelineElement

attr_accessor :source

def initialize
@fiber_delegate = Fiber.new do
process
end
end

def |(other)
other.source = self
other
end

def resume
@fiber_delegate.resume
end

def process
while value = input
handle_value(value)
end
end

def handle_value(value)
output(value)
end

def input
source.resume
end

def output(value)
Fiber.yield(value)
end
end

The process method is the driving loop. It reads the next input from the pipeline, then callshandle_value to deal with it. In the base class, handle_value simply echoes the input to the output-real filters subclass PipelineElement and subclass this method.


Let’s make a small change to the handle_value method.


    def handle_value(value)
output(transform(value))
end

def transform(value)
value
end

By doing this, we’ve split the transformation of the incoming value into a separate method. And the work done by this method no longer uses any of the state in the PipelineElement object, which means we can also do it in a block in the caller’s context. Let’s change our PipelineElement class to allow this. We’ll have the constructor take an optional block, and we’ll use that block in preference to thetransform. Here’s another listing, showing just the changed methods.


    class PipelineElement

def initialize(&block)
@transformer = block || method(:transform)
@fiber_delegate = Fiber.new do
process
end
end

# ...

def handle_value(value)
output(@transformer.call(value))
end
end

This illustrates a cool (and underused) feature of Ruby. Method objects (created with the method(...)call) are duck-typed with proc objects: we can use .call(params) on both. This is a great way of letting users of a class change its behavior either by subclassing and overriding a method, or by simply passing in a block.


With this change in place, we can now write transforming filters using blocks. This is a lot more compact that the previous subclassing approach.


    class Evens < PipelineElement
def process
value = 0
loop do
output(value)
value += 2
end
end
end

evens = Evens.new

tripler = PipelineElement.new {|val| val * 3}
incrementer = PipelineElement.new {|val| val + 1}

5.times do
puts (evens | tripler | incrementer ).resume
end

This outputs 1, 7, 13, 19, and 25.


Different Kinds of Filter


This approach works well if all we want is transforming filters. But what if we would also like to simplify filters that either pass of don’t pass values based on some criteria? A block would seem like a great way of specifying the condition, but we’ve already used our one block parameter up. Subclassing to the rescue. We can create two subclasses, Transformer and Filter. One sets the @transformer instance variable to any block it is passed. The other sets @filter. Here’s the relevant code:


    class PipelineElement

attr_accessor :source

def initialize(&block)
@transformer ||= method(:transform)
@filter ||= method(:filter)
@fiber_delegate = Fiber.new do
process
end
end

# ...

def handle_value(value)
output(@transformer.call(value)) if @filter.call(value)
end

def transform(value)
value
end

def filter(value)
true
end
end

class Transformer < PipelineElement
def initialize(&block)
@transformer = block
super
end
end

class Filter < PipelineElement
def initialize(&block)
@filter = block
super
end
end

Thus equipped, we can write:


    tripler          = Transformer.new {|val| val * 3}
incrementer = Transformer.new {|val| val + 1}
multiple_of_five = Filter.new {|val| val % 5 == 0}

5.times do
puts (evens | tripler | incrementer | multiple_of_five ).resume
end

Moving The Blocks Inline


Our final hack lets us move the blocks directly into the pipeline.


Let’s look at the actual pipeline code:


    puts (evens | tripler | incrementer | multiple_of_five ).resume

Those pipe characters are simply calls to the | method in class PipelineElement. And methods can take block arguments, right? So what stops us writing


    puts (evens | {|v| v*3} | {|v| v+1} | multiple_of_five ).resume

It turns out that Ruby stops us. The brace characters are taken to be hash parameters, not blocks, so Ruby gets its knickers in a twist. Fortunately, that’s easily fixed by making the method calls explicit.


    puts (evens .| {|v| v*3} .| {|v| v+1} .| multiple_of_five ).resume

Now we just need to make the | method accept an optional block. If the block is present, we use it to create a new transformer.


    def |(other=nil, &block)
other = Transformer.new(&block) if block
other.source = self
other
end

Ruby 1.9 lets you chain method calls across lines, so we can tidy up our pipeline visually.


    5.times do
puts (evens
.| {|v| v*3}
.| {|v| v+1}
.| multiple_of_five
).resume
end

A Palindrome Finder


Let’s finish with another trivial example. We’ll create a generic producer class that takes a collection and passes it, one element at a time, into the pipeline.


    class Pump < PipelineElement
def initialize(source)
@source = source
super()
end
def process
@source.each {|item| Fiber.yield item}
nil
end
end

Now we can write a simple palindrome finder (a palindrome is a word which is the same when spelled backwards).


    words = Pump.new %w{Madam, the civic radar rotator is not level.}
is_palindrome = Filter.new {|word| word == word.reverse}

pipeline = words .| {|word| word.downcase.tr("^a-z", '') } .| is_palindrome

while word = pipeline.resume
puts word
end

This outputs: madam, civic, radar, rotator, level.


But what if we instead want to show each word in the input stream, and flag it if it is a palindrome? That’s easily done, but we won’t do it the easy way. Instead, let’s show a more convoluted method, because it might be useful in the general case.


There’s no law to say that a transformer that receives a string as input has to write a string as output. It could, if it wanted to, write an array. Or a structure. So we could write:


    WordInfo = Struct.new(:original, :forwards, :backwords)

words = Pump.new %w{Madam, the civic radar rotator is not level.}

normalize = Transformer.new {|word| [word, word.downcase.tr("^a-z", '')] }

to_word_info = Transformer.new do |word, normalized|
reversed = normalized.reverse
WordInfo.new(word, normalized, reversed)
end

formatter = Transformer.new do |word_info|
if word_info.forwards == word_info.backwords
"'#{word_info.original}' is a palindrome"
else
"'#{word_info.original}' is not a palindrome"
end
end

pipeline = words | normalize | to_word_info | formatter

while word = pipeline.resume
puts word
end

This outputs


    'Madam,' is a palindrome
'the' is not a palindrome
'civic' is a palindrome
'radar' is a palindrome
'rotator' is a palindrome
'is' is not a palindrome
'not' is not a palindrome
'level.' is a palindrome

So, What’s the Point?


>


Is this a great way of writing a palindrome finder? Not really. But…


What we’ve done here is turned the way a program works on it’s head. We’ve written chunks of isolated code, each of which either filters or transforms an input. We’ve then independently knitted these chunks together. That’s a high degree of decoupling. We can also leave it until runtime to determine what gets put into the pipeline (and the order that it appears in the pipeline), which means we can move more power into the hands of our users.


Could we have done all this without Fibers? Of course. Could we do it without Ruby 1.9? Absolutely. But sometimes factors come together which lead us to experiment with new ways of thinking about our code.


This pipeline stuff is not revolutionary, and it isn’t generally applicable. But it’s fun to play with. And, for me, that’s the main thing.


A Wee Postscript


All this content is stuff that I decided not to include in the third edition of the PickAxe. It didn’t work in the section on fibers, because it uses programming techniques not yet covered. It didn’t work later because, as an example of various programming techniques, it is just too long.

Sunday, December 30, 2007

Pipelines Using Fibers in Ruby 1.9

Users of the command line are familiar with the idea of building pipelines: a chain of simple commands strung together to the output of one becomes the input of the next. Using pipelines and a basic set of primitives, shell users can accomplish some sophisticated tasks. Here’s a basic Unix shell pipeline that reports the ten longest .tip files in the current directory, based on the number of lines in each file:


 wc -l *.tip | grep \.tip | sort -n | tail -10

Let’s see how to add something similar to Ruby. By the end of this set of two articles, we’ll be able to write things like


puts (even_numbers | tripler | incrementer | multiple_of_five ).resume

and a palindrome finder using blocks:


words            = Pump.new %w{Madam, the civic radar rotator is not level.}
is_palindrome = Filter.new {|word| word == word.reverse}

pipeline = words .| {|word| word.downcase.tr("^a-z", '') } .| is_palindrome

while word = pipeline.resume
puts word
end

Great code? Nope. But getting there is fun. And, who knows? The techniques might well be useful in your next project.


A Daily Dose of Fiber


Ruby 1.9 adds support for Fibers. At their most basic, let you create simple generators (much as you could do previously with blocks. Here’s a trivial example: a fiber that generates successive Fibonacci numbers:


      fib = Fiber.new do
f1 = f2 = 1
loop do
Fiber.yield f1
f1, f2 = f2, f1 + f2
end
end

10.times { puts fib.resume }

A fiber is somewhat like a thread, except you have control over when it gets scheduled. Initially, a fiber is suspended. When you resume it, it runs the block until the block finishes, or it hits a Fiber.yield. This is similar to a regular block yield: it suspends the fiber and passes control back to the resume. Any value passed to Fiber.yield becomes the value returned by resume.


By default, a fiber can only yield back to the code that resumed it. However, if you require the "fiber"library, Fibers get extended with a transfer method that allows one fiber to transfer control to another. Fibers then become fully fledged coroutines. However, we won’t be needing all that power today.


Instead, let’s get back to the idea of creating pipelines of functionality in code, much as you can create pipelines in the shell.


As a starting point, let’s write two fibers. One’s a generator—it creates a list of even numbers. The second is a consumer. All it does it accept values from the generator and print them. We’ll make the consumer stop after printing 10 numbers.


    evens = Fiber.new do
value = 0
loop do
Fiber.yield value
value += 2
end
end

consumer = Fiber.new do
10.times do
next_value = evens.resume
puts next_value
end
end

consumer.resume

Note how we had to use resume to kick off the consumer. Technically, the consumer doesn’t have to be a Fiber, but, as we’ll see in a minute, making it one gives us some flexibility.


As a next step, notice how we’ve created some coupling in this code. Our consumer fiber has the name of the evens generator coded into it. Let’s wrap both fibers in a method, and pass the name of the generator into the consumer method.


    def evens
Fiber.new do
value = 0
loop do
Fiber.yield value
value += 2
end
end
end

def consumer(source)
Fiber.new do
10.times do
next_value = source.resume
puts next_value
end
end
end

consumer(evens).resume

OK. Let’s add one more fiber to the weave. We’ll create a filter that only passes on numbers that are multiples of three. Again, we’ll wrap it in a method.


    def evens
Fiber.new do
value = 0
loop do
Fiber.yield value
value += 2
end
end
end

def multiples_of_three(source)
Fiber.new do
loop do
next_value = source.resume
Fiber.yield next_value if next_value % 3 == 0
end
end
end

def consumer(source)
Fiber.new do
10.times do
next_value = source.resume
puts next_value
end
end
end

consumer(multiples_of_three(evens)).resume

Running this, we get the output


0
6
12
18
. . .

This is getting cool. We write little chunks of code, and then combine them to get work done. Just like a pipeline. Except…


We can do better. First, the composition looks backwards. Because we’re passing methods to methods, we write


    consumer(multiples_of_three(evens))

Instead, we’d like to write


    evens | multiples_of_three | consumer

Also, there’s a fair amount of duplication in this code. Each of our little pipeline methods has the same overall structure, and each is coupled to the implementation of fibers. Let’s see if we can fix this.


Wrapping Fibers


As is usual when we’re refactoring towards a solution, we’re about to get really messy. Don’t worry, though. It will all wash off, and we’ll end up with something a lot neater.


First, let’s create a class that represents something that can appear in our pipeline. At it’s heart is theprocess method. This reads something from the input side of the pipe, then “handles” that value. The default handling is to write that value to the output side of the pipeline, passing it on to the next element in the chain.


    class PipelineElement

attr_accessor :source

def initialize
@fiber_delegate = Fiber.new do
process
end
end

def resume
@fiber_delegate.resume
end

def process
while value = input
handle_value(value)
end
end

def handle_value(value)
output(value)
end

def input
source.resume
end

def output(value)
Fiber.yield(value)
end
end

When I first wrote this, I was tempted to make PipelineElement a subclass of Fiber, but that leads to coupling. In the end, the pipeline elements delegate to a separate Fiber object.


The first element of the pipeline doesn’t receive any input from prior elements (because there are no prior elements), so we need to override its process method.


    class Evens < PipelineElement
def process
value = 0
loop do
output(value)
value += 2
end
end
end

evens = Evens.new

Just to make things more interesting, we’ll create a generic MultiplesOf filter, so we can filter based on any number, and not just 3:


    class MultiplesOf < PipelineElement
def initialize(factor)
@factor = factor
super()
end
def handle_value(value)
output(value) if value % @factor == 0
end
end

multiples_of_three = MultiplesOf.new(3)
multiples_of_seven = MultiplesOf.new(7)

Then we just knit it all together into a pipeline:


    multiples_of_three.source = evens
multiples_of_seven.source = multiples_of_three

10.times do
puts multiples_of_seven.resume
end

We get 0, 42, 84, 126, 168, and so on as output. (Any output stream that contains 42 must be correct, so no need for any unit tests here.)


But we’re still a little way from our ideal of being able to pipe these puppies together. It’s a good thing that Ruby let’s us override the “|” operator. Up in classPipelineElement, define a new method:


    def |(other)
other.source = self
other
end

This allows us to write:


    10.times do
puts (evens | multiples_of_three | multiples_of_seven).resume
end

or even:


    pipeline = evens | multiples_of_three | multiples_of_seven

10.times do
puts pipeline.resume
end

Cool, or what?


In The Next Thrilling Installment


The next post will take these basic ideas and tart them up a bit, allowing us to use blocks directly in pipelines. We’ll also reveal why our PipelineElement class I just wrote is somewhat more complicated than might seem necessary. In the meantime, here’s the full source of the code so far.


    class PipelineElement

attr_accessor :source

def initialize
@fiber_delegate = Fiber.new do
process
end
end

def |(other)
other.source = self
other
end

def resume
@fiber_delegate.resume
end

def process
while value = input
handle_value(value)
end
end

def handle_value(value)
output(value)
end

def input
source.resume
end

def output(value)
Fiber.yield(value)
end
end

##
# The classes below are the elements in our pipeline
#
class Evens < PipelineElement
def process
value = 0
loop do
output(value)
value += 2
end
end
end

class MultiplesOf < PipelineElement
def initialize(factor)
@factor = factor
super()
end
def handle_value(value)
output(value) if value % @factor == 0
end
end

evens = Evens.new
multiples_of_three = MultiplesOf.new(3)
multiples_of_seven = MultiplesOf.new(7)

pipeline = evens | multiples_of_three | multiples_of_seven

10.times do
puts pipeline.resume
end

Wednesday, December 12, 2007





Ruby 1.9 is just around the corner, so it looks like a good time to create a new edition of Programming Ruby. So, I’m pleased to announce that the Third Edition of the PickAxe has just entered beta.


The book’s home page is at http://pragprog.com/titles/ruby3.


Although 1.9 is largely compatible with 1.8, there are definite differences. And it’s been an interesting ride getting the examples in the book to compile and run with the current 1.9 interpreter. The book pushes the envelope in many different areas, and includes example code designed to illustrate edge cases. When I find these, I’m flagging them in the text and (if they look like bugs) adding them to the tracking system. But, so far, 1.9 is looking like a big win for Ruby.


Nice job, everyone.

Thursday, November 8, 2007

Michael Swaine on Computer Book Publishing

I few weeks back, Michael Swaine emailed asking for our impressions of the computer book publishing situation. He also checked in with some other publishers. His conclusions are posted in a new online Dr Dobbs article.


I know that traditional publishing will never die—people just like holding paper books too much. But the reality of the new world will continue to drive publishers to find new ways of getting information out there. That’s why our business is successful. We started by applying software development project techniques to book creation (version control, DRY, testing, etc). That gave us the lowest cost model of any publisher I know, which in turn let us accept proposals because we wanted to see the book published, and not because some marketing model told us it would sell. We tried other innovations, such as applying agile principles to publishing, gaining early and continuous feedback with our beta book program. And we’ll continue to innovate in the production of books—I still get too much of a thrill from holding the “fresh-from-the-printer smelling first copy of one of our paper books not to.


But, underneath it all, we’re not really book publishers. What drives us is helping the community we know: software developers. The initial starter kit series was simply a way of spreading the information that we gave to teams during our consulting gigs to a wider audience. The Ruby and the Rails books were just a way of sharing a passion. The Erlang book, and books such as Release It! and Design Accessible Web Sites highlight technologies and techniques that we think are cool, and that we think will help other developers. We choose to get that information out in book form because we feel that the process of creating a book helps distill and refine the information. It lets us add value as editors.


I think the problems of the publishing industry will turn out to be a good thing. People get complacent during good times. When when times get harder, the folks who survive are the ones who focus in on the core of what they’re doing. All the publishers still in the field are experimenting and innovating as they refine their focus.


Our focus is a passion for software development and a desire to help software developers have more fun while doing one of the most challenging jobs there is. I’m having a blast.

Tuesday, October 16, 2007

Art in Programming

Andy and I gave a talk a JAOO a few years back called The Art n Programming. Since then, I’ve been giving variants around the world, talking about the ways in which developers can learn from artists (and, along the way, how we should avoid the false dichotomy of art vs. engineering).




David Troy came up to me after and said that he’d heard that the Museum of Modern Art in New York might be featuring two of his Rails applications in an exhibition.


Now it’s officialTwittervision and Flickrvision will be on display next year.


In his blog announcement, David says:


Today, creativity and imagination (what some folks are calling the right brain) are becoming the key drivers of software and design. With imagination, we can see around the corners of today’s most pressing challenges. While technical skill is certainly valuable, if it’s applied to the wrong problems, it’s wasted effort.

Creativity, imagination, and artistry help us identify the areas where we should put our efforts. They help us see things in new ways.




Hear hear! I like to think of it as bringing balance back to a process that for too long has been seen as some poor bastard child of mathematics and engineering.


Software development is neither. Nor is it art. It’s just software development. People who look for the “software is like xxx" analogies are missing the point. Software develpment is like software development. Let’s decide what works for us, and have fun while doing it.



Wednesday, August 29, 2007

Superators: neat Ruby hack

imageImage stolen from jicksta.com

So, I’m not sure if Jay Phillips (of Adhearsion fame) is trying to annoy the purists with a remarkably Perlish hack in Ruby. I just know that his superators gem is a clever bit of pure Ruby code that allows you to apparently define new infix operators in Ruby programs. Imagine the joy of being able to write


a /~ b

and


rocket <=- fuel

You can with superators:



require 'rubygems'
require 'superators'

class String
superator "<=-" do |arg|
self << arg.upcase
end
end

Is this a good idea? I don’t know. I can see that it would have been useful in some DSLs that I’ve written. But I do think that it’s a wonderfully clever implementation (it doesn’t do anything nasty to the interpreter–it simply intercepts built-in operators (such as <<= and -) in my example above. And that’s what makes it great. Every now and then, just when you think you know Ruby. someone comes up with something—such as symbol.to_proc or superators—that makes you go “oh!.”


Every long-lasting relationship needs the occasional surprise to keep it interesting and fun. This kind of hack is one of the reasons I love Ruby.

Sunday, August 12, 2007

Possibly my favorite Rake target

rake gerbils:deploy


Cry “Havoc!” and let slip the rodents of PDF generation.