ACTUAL Duck Typing Advantages!!
Yes, my friends, I have found what could be a true unique advantage to duck typing. I don’t know why this didn’t occur to me. Everyone else may already realize this, perhaps it’s been diced on ruby-talk already, but it’s just dawned on me. Singletons and duck typing.
Get it in your head: singletons and duck typing. See if you can figure it out. This is a concept that could be a central figure in the way we think as we scrawl out our Ruby.
Open-URI
I’m going to use the open-uri library as an example, because the discussion of this library on the ruby-core list is what made it click for me.
If you haven’t used open-uri, you can read about it in What’s Shiny and New. It’s a little library that allows you to open URIs (http, ftp) as if they were files.
open( "http://www.whytheluckystiff.net/why.yml" ) do |f| feed = YAML::load( f ) end
The discussion on ruby-core has revolved around adding a progress bar hook to the open-uri library. A recent syntax suggestion looks like this:
progress = proc { |s| ... }
open( "http://...", OpenURI::OptProgressProc => progress )
In the example, I’m passing in a proc through an options hash. This is common in many languages. Especially in Perl, you’ll see a hash of options follow the fixed parameters. It’s (sort of) a substite for named parameters.
The above code coats my eyes with sap. My vision could get fully encrusted by that syntax. No criticism to the authors of this code. This is a very common technique in Rubyville. And to its benefit, it can be easy to use.
But when you’re passing procs into hashes into methods, it starts to seem a bit thick.
Singletons and Duck Typing
Lately, I’ve been trying to solve problems with duck typing. Not because I think it’s the universal panacea, but because I’d like to see how far it can stretch. I’ve been sort of skeptical at time and for that I will assuredly pay.
I looked at this progress bar example and it occured to me, “This is a perfect situation for duck typing.” We want to change the behavior of OpenURI. We want it to quack periodically while the file is downloading, right? Why are we embuing behavior through an options hash?!
Behavior is defined by methods in Ruby. They are our stage directions. Procs are a building block of this, but they should exist within methods to perform finer, atomic actions. We shouldn’t be moving our instructions out of methods! Is this correct?
This example is going to start off a little rough. My previous examples used Kernel::open, which open-uri provides. I don’t have an answer for extending Kernel::open to become the ultimate URI/File/mayonaisse jar opener. I think Kernel::open should have it’s functionality limited. But I may address this later as the idea dissipates across the folds of my brain.
Instead, let’s imagine using the URI class, which can be instatiated, to solve our problem.
uri = URI.parse( "http://whytheluckystiff.net/why.yml" )
We now have a URI object. What if we could just add a new singleton method for a progress bar?
def uri.progress_bar( percent_done ) puts "Yeah, we're at " + percent_done + "% now!" end
The URI::progress_bar method would not be built into the URI class. It’s not a required part of processing the transmission. But the URI class could still use respond_to to check for it and use it if available.
So, the class could duck type in case singletons have the method available. Our completed example could be:
uri = URI.parse( "http://whytheluckystiff.net/why.yml" )
def uri.progress_bar( percent_done )
puts "Yeah, we're at " + percent_done + "% now!"
end
uri.open { |f| ... }
Singletons For Hashes
You could also use singletons in lieu of option hashes. This could solve the Kernel::open issue. It feels like a design pattern. Not sure which.
opts = OpenUriOptions.new def opts.progress_bar( percent_done ) puts "Yeah, we're at " + percent_done + "% now!" end open( "http://whytheluckystiff.net/why.yml", opts )
In the Kernel::open code, you could check the ducky opts var:
if opts.respond_to? :progress_bar opts.progress_bar( percent_done ) end
You may say, well how does this differ from procs? Well, I think methods tend to be a lot more maintainable. If you came up with a robust class for progress bars, a fully featured extension to imaginary OpenUriOptions class, you could include slap the new class on RAA. It is encapsulated. It is reusable. We just don’t do everything in Ruby with procs.
I haven’t milked this idea fully yet. Help me with it. Send me your hybrid ideas and let’s flesh this out fully.
