Bindings, Platforms, and Innovation
This presentation focuses on the Internet and separating myth from fact, history from the future, and the mundane from the imaginative. Bob Frankston presents a vision of what could and should be.
Tracking change and innovation in the enterprise software development community
Posted by Werner Schuster on Jul 30, 2007 09:00 AM
Piers Cawley writes about a potential problem he discovered in a blog article about Lazily Initialized Attributes. The problematic code:def contentThis is aimed to allow for lazily initialized attributes of a class. In this case, the
@content ||= []
end
@content instance variable is initialized with [] when it's accessor method content is called, unless it has already been initialized. The ||= operator means "if the left hand variable is nil, set it to the right hand value, otherwise just return the left hand variable's value". nil. Here an example to illustrate:
a = falseWhat's the result of this? Since a was initialized in the first line, the second line should not have had any effect. However, executing that code reveals that
a ||= "Ruby"
a now has the value "Ruby", instead of false. nil checks in Ruby:
if nameIn Ruby, a
puts name.capitalize
end
nil is interpreted as boolean false, so the code in the if clause will only run if the name is not nil. nil or false. In that case, an access would reset the variable to it's default value. def contentThis only initializes the variable if the variable hasn't been defined yet.
unless instance_variable_defined? :@content
@content = []
end
return @content
end
||= wasn't the right solution and instead the initialization code is supposed to check if the variable had been defined yet. Download the Free Adobe® Flex® Builder 3 Trial
Effective Management of Static Analysis Vulnerabilities and Defects
Agile Development: A Manager's Roadmap for Success
Ensuring Code Quality in Multi-threaded Applications
The Agile Business Analyst: Skills and Techniques needed for Agile
Nice post. Those bugs are nasty to find. "... it's widely known which type of workers blame their tools ..." makes me laugh though. I know a lot of people who blame Java because of their lack of productivity. And I know very few people who still write Mc68k/40 code. Peace -stephan -- Stephan Schmidt :: stephan@reposita.org Reposita Open Source - Monitor your software development http://www.reposita.org Blog at http://stephan.reposita.org - No signal. No noise.
I've often thought we need more metadata on our variables (or function arguments, or properties, etc). Currently we have just one: null. A reference can either be null, or have a value. I'd like to also have "defined" to say if a given name has been defined. "Initialized" can say if it has ever been set before, in which case the value could also be nil or non-nil. Also "null" can work just like it does now (or nil in Ruby, whatever). A hybrid "has value" concept would be nice too, which requires both "initialized" to be true and also "is null" to be false. With these constructs it would be possible to write very clear expressions such as "if x is initialized ..."
def content
@content ||= []
end
In what case could something return an array or false- n valued logic? Seems completely off base as a criticism.
100% of the time I use code like this it's to avoid doing nil? checks all over the code. If you are writing code where nil is an acceptable value, why would you need to initialize the array to empty? It seems odd to have a semantic difference between nil and empty array where nil is something that the array explicitly gets set to after it is initialized. A programmer would generally expect that nil means uninitialized. Now I have to check for nil every time I call:
content.each
In the case where you are actually dealing with a boolean, you don't have to do anything- the nil naturally works in conditional expressions.
Maybe I just need a better example to understand this particular point. I agree with the general thrust of the article- that sometimes being concise can obscure the meaning of the code, but I don't see this idiom as problematic. On the other hand- having 0 != false in Ruby is a bit weird, no pun intended.
The original article on "Lazily Initialized Attributes" ( http://blog.jayfields.com/2007/07/ruby-lazily-initialized-attributes.html ) where this issue was found, shows a general way to initialize attributes only when they're actually accessed.
So, this is not specific to lists, but generally to using this idiom for initializing attributes.
So, if you use this idiom to create a boolean attribute or an attribute that can be some value OR nil, then you can run into trouble.
Eg
class Foo
def red?
@red ||= true
end
def red=(arg)
@red = arg
end
end
x = Foo.new
x.red? # returns true, default value
x.red = false # @red is now false
x.red? # returns ... true, because of the issue described in the article
The trickiness in Jay's case is trying to lazy initialize a boolean to true. That's the only place one could actually run into a problem, but a simple nil check would work there. I still think the standard @x ||= default
works except for cases where you want to initialize a boolean.
In any case, lazy initialization should probably only used for expensive operations, not for setting simple defaults.
I think the distinction between nil? and empty? or blank? (a Rails addition) is more interesting area to explore. I think using or implementing those methods makes things more clear than setting something to nil and expecting that to mean empty, as opposed to uninitialized.
My point was that generic code should always consider the corner cases. Espcially with a pattern like this which can so easily be wrapped up in a method constructor. In code where you control the horizontal and the vertical there's nothing wrong with taking the view that you'll never trip over the nil/false problem, but in library code you must account for it. Since a pattern is essentially a subroutine that's executed by a human being, it makes sense for the pattern to at least discuss the potential pitfall.
I have to admit to using ||= absentmindedly. Thanks for pointing out the gap in this idiom. I'm sure it was only a matter of time before I was bit by it.
This presentation focuses on the Internet and separating myth from fact, history from the future, and the mundane from the imaginative. Bob Frankston presents a vision of what could and should be.
This article explores the use of JBoss and jBPM to implement design solutions that effectively address the issue of orchestrating long running activities.
This presentation covers the use of graph databases as an optimal solution for data that is difficult to fit in static tables, rapidly evolving data or data that has a lot of optional attributes.
This session introduces Real Options and shows how it can help in running your project. Real Options is a decision-making process that can be used to manage risk.
This article discusses the use of bindings on services and references (including the instance of non-configured bindings) as the means to implement SCA communications in a Web and SOA environment.
After a short introduction to DSLs, Scott Davis plays with the keyboard showing how to approach the creation of a DSL by typing working snippets of Groovy code that get executed.
IBM Rational and InfoQ present, Scaling Agile with C/ALM, an eBook showing organizations how to become “finely tuned software delivery machines” by enabling team integration and scaling.
Amanda Laucher presents a real life enterprise application written in F#. She shows actual code snippets, explaining design decisions and suggesting how to use some of the F# constructs.
8 comments
Watch Thread Reply