Archive

Monthly Archives: August 2011

Turns out the Internet was right on this one: if you’re trying to create a big string from little ones it’s faster to << (less-than-less-than operator) on a String than to make an array of Strings and join its elements. Without diving into the Ruby code itself my guess is that strings are linked lists in memory and its end pointer is just moved, whereas making a bunch of array elements means new String objects for each and then having to walk all of the objects to form a new String in the end. (Granted, I’m using Ruby 1.8.7 still!)

Here’s the code:

require 'benchmark'
Benchmark.bm(20) do |x|
  x.report('join') do
    1_000.times do
      a = []
      1_000.times do
        a << 'This is some string stuff'
      end
      a.join
    end
  end
  x.report('<<') do
    1_000.times do
      a = ''
      1_000.times do
        a << 'This is some string stuff'
      end
      a
    end
  end
end

And the result:

          user     system      total      real
join  1.180000   0.390000   1.570000 (1.571057)
<<    1.090000   0.390000   1.480000 (1.491420)

While listening to The Ruby Show episode #176 I heard about the ThoughtBot post about the Maybe Pattern as discussed in their “If you gaze into nil, nil gazes also into you” post.

It took me a minute to figure out what was going on. (Maybe I’m just getting slower in my old age.) Plagiarizing from their post, they included the code required to set up the Maybe Pattern and I’ll try to add my own comments in red so I feel like I’m adding something to this conversation:

This is modifying the base Object of which every Ruby object is
a child. Yes, that means even Nil gets an automatic maybe()
method stuck in it.

The idea is that if you think a method *might* return a value
then you ought to return your value with a .maybe() on it.
That accomplishes the "wrapping". You'll see that it takes
itself and returns an instance of Some with itself stuffed
into it. Thus at this point you really get a Some() object
back, not your original return value.
class Object
  def maybe
    Some.new(self)
  end
end

... Except for Nil. This one definitely is empty/blank/nothing
so we need to create an instance of Some that will throw
exceptions if you try to unwrap it.
class NilClass
  def maybe
    None.new
  end
end

And here is the Some object that wraps anything you give
it by storing it in its own @object attribute.
class Some
  def initialize(object)
    @object = object
  end

  This is how you "unwrap" an object. Pay close attention
that the None version of this will throw exceptions.
  def get
    @object
  end

  This is one way you can check to see if there might
be values.
  def present?
    true
  end

  And this is the other.
  def blank?
    false
  end
end

And back to some Magic Sauce. It creates a new error type,
Unwrapped, and will return it when you attempt to call get().
This is because as a consumer of the Some or None values
you ought to have checked present?() or blank?() first. That's
right, consumers need to be sure that they're not just going
to use a nil value.
class None
  class Unwrapped < StandardError; end

  def get
    raise Unwrapped
  end

  def present?
    false
  end

  def blank?
    true
  end
end

OK, great. So how do you use this in practice?

When you return a value that is absolutely true or false, then do so. If you think your return value might be null then wrap it by tacking on a .maybe() call.

The consumer of these return values should always check for present?() or blank?(). If something is not present or is blank then you should no longer need to access the return value because, well, you got your answer.

However, if you need to use the return value for something (e.g. it might be a single object, or might be an array, or a Hash, or ...) then you need to check if it's present?() and/or not blank?(). If that's true then you can call the object's .get() method to grab that value and continue with your logic. Like:

accounts = bank(me).accounts
# accounts might be nil ... check present?()
if accounts.present?
  account_list = accounts.get
  # Now account_list definitely has something which isn't nil!
end

The main thought I had in all of this is that this will create tons of wrapper objects that could just add bloat to your application. Plus there's the extra evaluation involved. In my own code I almost always just use the Rails .blank?() method to check if things are there. That seems to handle nil, empty strings, and empty arrays just fine!