Just A Summary

Piers Cawley Practices Punditry

Deja vu all over again

Back when I was still programming Perl, one of the common mistakes that you’d see when people wrote lazily initialized object accessors was code like:

sub get_content {
my($self) = @_;
$self→{content} ||= ‘default content’;
}

Code written like this would trundle along quite happily, until you had an attribute where, say, the empty string or 0 were legal values for the attribute. The problems were especially painful when the default value wasn’t something that perl treated as false. The correct way of writing that code would be:

sub get_content {
my($self) = @_;
unless (exists($self→{content})) {
$self→{content} = ‘default content’
}
$self→{content}
}

Which is substantially uglier, but safe.

Safety’s important, especially in building block code like accessor methods. An accessor method that works 99.99% of the time is like a compiler that produces correct code 99.99% of the time – useless.

Why déjà vu?

Recently, the usually spot on Jay Fields wrote up the lazy initialization pattern for Ruby. I’m not entirely sure that I agree with his motivation for the pattern, but I am concerned by his suggested code transformation. He suggests writing your lazy initialization as:

def content
@content ||= []
end

Does that look familiar? This is subject to exactly the same potential bug as the perl code above. Admittedly, the number of possible ‘bad’ values is reduced to nil and false, but it only takes one. Here’s the fix:

def content
unless instance_variable_defined? :@content
@content = []
end
return @content
end

This code is guaranteed to work in all circumstances.

Going a little bit further…

As Mark Jason Dominus has argued persuasively elsewhere, patterns are a sign of weakness in a programming language, so how can we go about incorporating this boilerplate code into our language1.

How about something like this (as yet untested):

class Module
class << self
alias_method :attr_reader_without_default_block, :attr_reader
def attr_reader_with_default_block(args, &default_block)
unless block_given?
return attr_reader_without_default_block(
args)
end
unless args.size == 1
raise ArgumentError, “Expected 1 argument”
end
var_name = “@#{args.first.to_s}”
self.define_method(args.first) do
unless instance_variable_defined?(var_name)
self.set_instance_variable(var_name, default_block.call(self))
end
(class << self; self; end).attr_reader_without_default_block(args.first)
return self.send(args.first)
end
end
alias_method :attr_reader, :attr_reader_with_default_block
end
end

This code is a little more complex than the boilerplate code. When the generated method is called, possibly initializing the attribute, the (class << self; self; end).attr_reader_without_default_block(args.first) part replaces the instance’s accessor with the default attr_reader implementation and calls that instead. This is arguably premature optimization, but it’s not all that evil…

Assuming I’ve not screwed anything up, that should allow you to write.:

class Article
attr_reader :content {’default content’}
end

and have your content lazily initialized. Extending this to let attr_writer take a block too is a reasonably obvious next step.

Extending this lazy initialization approach to work with ActiveRecord based classes is probably the next step after that. Making it work right probably involves a little bit of fossicking around in the workings of ActiveRecord, but it’s far from impossible.

1 I tend to take the Smalltalkish view that it’s pointless to separate language from library, especially in a dynamic language. A sufficiently expressive language lets you blur the boundary between them.

Published on Sun, 29 Jul 2007 18:18:00 GMT by Piers Cawley under . Tags

If you liked this article you can add me to Twitter
  • Gravatar

    By Jay Sun, 29 Jul 2007 21:16:03 GMT

    I updated the example from my entry. Thanks for the tip.

    Cheers, Jay


  • Gravatar

    By Aristotle Pagaltzis Mon, 30 Jul 2007 00:44:41 GMT

    This is a thorny problem because exists must return a false value when the key does not exist, so there is no way it could possibly return something to assist with brevity in the rest of the code. An additional complication is that treating a non-existent key as a hash value in any way (such as by taking a reference to it) automatically creates the corresponding key, defeating the purpose of using exists in many cases.

    What annoys me is that exists looks like a function, but is half an operator: what comes after must look like a hash look-up, but a hash lookup does not happen – rather its operands are passed to exists. This magical parser behaviour is not available to user code, which makes it impossible to approach the conciseness of “$hash->{key} ||= 'default'”.

    Of course this is as easy to fix in Perl as in Ruby, for particular use cases (just as in your example). But it’s very hard to come up with a generic solution in any language whose semantics do not include lazy evaluation.

    PS.: what’s that “@end@” doing in your second Perl example? And surely you mean “if ( <strong>not</strong> exists $self->{ content } )”?


  • Gravatar

    By Philip Newton Mon, 30 Jul 2007 02:55:36 GMT

    Which is substantially uglier, but safe.

    Well, I suppose it’s safe as long as you have a source filter that turns “end” into a closing curly bracket so that the parser is happy.

    Also, don’t you want a “!” in front of the “exists”? Otherwise you’ll replace the content with default content exactly when there already was content — but if it was uninitialised, you won’t.


  • Gravatar

    By Daniel Berger Mon, 30 Jul 2007 13:12:17 GMT

    Ugh. Instance variables should be initialized in the constructor. Always.

    I utterly disagree with Jay on this issue, for precisely the reasons you state here.


  • Gravatar

    By Piers Cawley Mon, 30 Jul 2007 14:13:23 GMT

    I wouldn’t go so far as to say “always”. With many of these decisions it’s a trade off. My inclination is to come up with a more declarative way of specifying attributes and their defaults.

    I’m currently toying with implementing something along the lines of:

    attributes do
    comments do
    default { [] }
    type :array
    end

    body.default(“Fill in the blank”) body.should_not be_empty

    end

    The idea being that you can specify most things about your attributes in a declarative way, which means you don’t have to worry about the details of how they’re initialized, whether it be lazily or within the constructor. If the fancy took you, you could even arrange things so that the attributes are completely encapsulated by playing closure tricks

    def generate_attribute(attrib, &block)
    uninitialized = Object.new
    generator = lambda do |the_attrib|
    define_method(attrib) do
    case the_attrib
    when unintialized
    yield
    else
    the_attrib
    end
    end
    define_method(“#{attrib}=”) do |newval|
    the_attrib = newval
    end
    end
    generator.call(uninitialized)
    end

    The trick here is that it’s impossible to get at the value of the attribute except through the accessors because the value itself is only in scope within the two closures which define the accessors. Caveat coder though, the above is completely untested.


  • Gravatar

    By Daniel Berger Mon, 30 Jul 2007 21:38:57 GMT

    Although that’s a neat trick to prevent access through anything but an accessor Piers, I personally find it much too clever.


  • Gravatar

    By Piers Cawley Tue, 31 Jul 2007 02:01:13 GMT

    Not that clever – I just realised that, as written it’ll only work for per class attributes. To make it work for instances you have to generate the accessors when you create the object, and to do that transparently you have to get really clever. You know you’re heading down a dark path when you start considering overriding allocate.

    Definitely heading off into the realms of Bad Clever.

    But, generally, I don’t think taking advantage of closures is a bad thing to do.


  • Gravatar

    By Aristotle Pagaltzis Tue, 31 Jul 2007 19:25:56 GMT

    Heh. I guess this is one of those rare cases where Perl’s lack of built-in data inheritance makes things easier. Such functionality would be trivial to either custom-write or to retrofit to your method maker module of choice.


  • Gravatar

    By Piers Cawley Wed, 01 Aug 2007 04:50:47 GMT

    I think the very first OO documentation for Perl 5, way, way back in the day discussed implementing objects using blessed closures that worked analogously to the way Lisp and Scheme does objects:

    sub new {
    my($class,$attrib) = @_;
    my $inst = sub {
    $attrib = shift if @_ == 1;
    $attrib
    };
    bless $attrib, $class
    }

    sub attrib { $0→() }
    sub set_attrib { $_0→($
    1) }

    Obviously a ‘real’ implementation of that would probably do a little more sanity checking…

    However, this was discouraged as a method of doing this stuff by virtue of the fact that made and already slow dispatch process even slower by layering on another function call into the closure to get at the attribute…


Comment Deja vu all over again

Trackbacks are disabled

Powered by Publify – Thème Frédéric de Villamil | Photo Glenn