It’s Wednesday, according to my watch. The top of the screen agrees, so let’s call it Wednesday. Today, I’d like to experiment with the Ruby “lambda” or block capability.

We could probably pull out some fancy math or functional programming terms here but a “lambda” is just a block of code with one or more parameters. We use them all the time in Ruby, but they have some tricks we might not use often. Here’s an example from our existing code:

    @items.each do |item|
      update_item(item)
    end

The block is this part:

    do |item|
      update_item(item)
    end

The do-end delimits the block and the |item| indicates that the block’s parameter is to be named item inside the block. The block is an “anonymous function” with the parameter item. It’s anonymous because it has no name, and a function because … well, because that’s what it is.

There’s some interesting stuff about what variables the block can reference and the like, but with few exceptions, they work in a way that won’t surprise you if you’re comfortable with them in the usage here.

Let me say a bit about what I’m up to here. Based on our work so far, each different kind of item needs only a couple of lines of code to update it. Our main inherited update method isn’t too awful, just a series of if-elsif statements. Our original wrapper idea – OK, my idea, you aren’t to blame if it turns out bad – was to build a tiny class for each kind of item, wrap the raw item with it, and call its update.

Given where we are now, we should probably either go forward with that scheme, extracting from our if nest until it’s all gone, or we should go backward, inserting our new conjured item into the if nest. It’s surely better if we do the thing one way, not two.

Either of those choices is straightforward, and unless I come up with something I want to say about it, I may not even do it in an article. “Left to the reader”, as they say.

But I have an intuition, or a wondering, about another way. Could we have a collection of updating blocks, essentially the few lines of code that would be in the update method of our class approach, or in the specific if nest of the other approach, and then select the block and execute it? Surely we could. If we could, would it be better than the if nest?

Now in all honesty, I’m not sure we can beat the if nest, now that it comes down to a couple of lines and some helper methods. If I’m successful here, even with the best success possible, I expect not to like the result better than either one of our possible end points, all in the if nest, or all in separate classes.

I’m expecting to fail. But here we are, the team (the cat and I) have this really cool idea we read about, and we want to try it. We have a lambda hammer and this program looks like a nail. So we decide to learn a bit and then decide what to do. We plan not to commit this code even once, unless it seems to be working. If we were the kind of people who use branches, we’d do this on a branch. But we’re not.

So. Let’s fail.

Parts of the idea

Now I know you think I’m selling “just start programming and refactor until good”, but the truth is I do think about what I’m setting out to do. If you read these articles, you’ll see that I always have a bit of a plan about what I’m going to do. I just don’t spend a lot of time planning, and I don’t really believe the plan beyond a general “go west, old man” kind of vibe.

Here, I see these issues:

The Update Lambdas
We’ll have a bunch of little functions, taking the item as a parameter, doing the couple of lines we presently have in our if nest or conjured class.
Selecting the Lambdas
We’ll use our match approach to decide what kind of item we have, and given that match, select the function to apply. That suggests to me a table of match-string and corresponding function.
Getting the Lambdas into the Table
We could probably build up the table longhand, with the little lambda functions all right in line, but that might get pretty ugly. We’ll probably define them separately and then reference them into the table.

I have only a vague idea how to do this. To my recollection, I’ve never done anything quite like it before, at least not in Ruby.

So I’m expecting lots of trouble, and lots of undoing. Enough chatter. Here goes. Let’s look for a place to do this and a simple one to try.

The main Gilded Rose class looks like this:

class GildedRose

  def initialize(items)
    @items = items
  end

  def match_name(item,name)
    return item.name.downcase.include? name.downcase
  end

  def appropriate_wrapper_class(item)
    return ConjuredWrapper if match_name(item, "conjured")
    return OriginalWrapper
  end

  def update_item(item)
    wrapper_class = appropriate_wrapper_class(item)
    wrapped_item = wrapper_class.new(item)
    wrapped_item.update
  end

  def update_quality()
    @items.each do |item|
      update_item(item)
    end
  end
end # Gilded_Rose

Here we select a class with which to “wrap” our current item, instantiate the class, send it update, and move on. Pretty simple, really.

If instead of classes we were selecting blocks of code to call, it would look similar but different.

I’m honestly having trouble seeing quite how I want to hold this thing while I work on it. Probably you already know but I don’t even know where my pair, Kitty, is at the moment.

Let’s think about Brie. To update brie, we need to do roughly this:

my.sell_in -= 1
my.quality += 1 unless my.quality >= 50
my.quality += 1 unless my.quality >= 50 if expired?

I expanded the code for the incrementation, at least for now. If we wind up with it looking like that, we may consider the duplication to be a problem and look to remove it. That’s OK, for now I want to keep things straightforward until the new pattern either takes shape or gets thrown away.

So there’s a patch of code that I can put in a lambda somewhere, if I can get the syntax right. Then maybe I can arrange to call it. Note that it only references an actual item named my. Let’s try to make something sort of work.

I managed to make this work:

  def update_item(item)
    brie_lambda = ->(my) {
      my.sell_in -= 1
      my.quality += 1 unless my.quality >= 50
      my.quality += 1 unless my.quality >= 50 if my.sell_in < 0
    }
    if match_name(item, "brie")
      brie_lambda.call(item)
    else
      wrapper_class = appropriate_wrapper_class(item)
      wrapped_item = wrapper_class.new(item)
      wrapped_item.update
    end
  end

The good news is that the tests pass that way. The bad news is, it’s surely not better than anything except, just possibly, the original code. Carried forward, we’d have all these odd lambdas, which we might as well just paste into the if statement, and we’re back with our if-nest approach.

That’s a legitimate outcome of this experiment, but I haven’t learned enough about what needs doing. I’d really like a table of match-string and lambda, probably stored as a member variable in this class, GildedRose. Let’s just try typing in a table.

It turns out that this works:

class GildedRose

  def initialize(items)
    @items = items
    @lambdas = {}
    brie = ->(my) {
      my.sell_in -= 1
      my.quality += 1 unless my.quality >= 50
      my.quality += 1 unless my.quality >= 50 if my.sell_in < 0
    }
    @lambdas["brie"] = brie
  end

  def match_name(item,name)
    return item.name.downcase.include? name.downcase
  end

  def appropriate_wrapper_class(item)
    return ConjuredWrapper if match_name(item, "conjured")
    return OriginalWrapper
  end

  def update_item(item)
    if match_name(item, "brie")
      @lambdas["brie"].call(item)
    else
      wrapper_class = appropriate_wrapper_class(item)
      wrapped_item = wrapper_class.new(item)
      wrapped_item.update
    end
  end

  def update_quality()
    @items.each do |item|
      update_item(item)
    end
  end
end # Gilded_Rose

I couldn’t find a way to write the lambda right in line with the table defintion, so I had to save it to a variable and then put it in the table. After a little searching, I found this possibility:

  def initialize(items)
    @items = items
    @lambdas = {}
    @lambdas["brie"] = lambda { |my|
      my.sell_in -= 1
      my.quality += 1 unless my.quality >= 50
      my.quality += 1 unless my.quality >= 50 if my.sell_in < 0
    }
  end

OK, that’s nearly good, and probably about as good as it gets.

I think this is enough to tell me what an implementation around lambda would look like. It’d be a big table of string->function, and we’d call the first function whose string matched. (We can’t just look up into the table, since our match string doesn’t really want to be the whole name.)

We wouldn’t want to code that all in line, so we’d have a function, maybe find_appropriate_lambda and we’d loop over items, finding the lambda and calling it.

What we would do now if we go forward with the wrapper classes is exactly that, we find the appropriate class and use it. With the lambdas, we’d perhaps save building an entire class, at the cost of using a more obscure technique. We could probably fold up common elements like the quality update unless 50 and such into helper methods in the GildedRose class.

In the end, it would look nearly the same, perhaps a little shorter, certainly more weird.

Summing Up

So this has been an interesting learning experience. I don’t think I’ve begun to tap the intricacies of lambdas and blocks, but I’ve scratched the surface and made them work just a bit. What we call a “spike” in XP.

And of all the approaches I’ve tried, I’m coming to prefer the most straightforward, a basic if-else kind of structure in a single update method, calling custom update methods for each type of item.

This choice would not have been possible, had we not gone through the tedium of unwinding the original horrid update method. And creating our original ConjuredWrapper was worthwhile as well, because that was where we began to see common elements in updating.

So, what’s to do. Well, I’m going to revert out all these changes. Some teams might save them in a branch, but I’m going to toss them. I could immplement them again if I ever need them, and I don’t expect to.

Then, perhaps in a spirit of completeness and being locked in the house anyway, I’ll probably push the program all the way back into an if-nest approach.

We’ll see. Tomorrow is another day. I hope.