On Jun 20, 2012, at 07:43 , Jan E. wrote:
I kinda feel like I’m being called out as I’m on record (many times) for
2/3rds of your examples so I’ll address them specifically:
“Don’t concatenate strings, use string interpolation instead”
Using a recent real example from this list where I suggested
interpolation:
m.ClassMethodString + " " + m.ClassMethodString + ": " +
m.ClassMethodInteger.to_s
mmmmm java code.
- slower – several more method calls
- wasteful – creates much more garbage
- longer/uglier – I’d argue that it is much less elegant
vs
"#{m.ClassMethodString} #{m.ClassMethodString}:
#{m.ClassMethodInteger}"
- clarity – it’s just a string.
- elegance – it’s JUST a string AND you don’t need those stupid #to_s
calls.
- efficient – takes less time, uses less memory, makes less garbage,
and even easier to read.
“Don’t use Enumerable#inject to build up objects” etc. etc.
Given #inject’s other alias, #reduce, it is obvious that you don’t use
#inject for building up other objects. Even in a functional style of
programming you’d never see it building up anything. You’d see it
REDUCING (folding) an object. If #inject is applied in a non-folding
manner, it isn’t functional, it is just dumb. Don’t pretend otherwise
(and if you do pretend otherwise, go read more books on lisp–start with
SICP). The second I see a semicolon (or return) in an inject, I
immediately suspect that someone is writing clevar/stupid code.
I don’t have any recent examples from the list, but I’m on record in
multiple mediums ranting against people who use #inject improperly. I’ll
make up one based on examples I’ve seen time and time again:
return im_a_lazy_bastard.inject(Hash.new 0) { |h, o|
h[o.really_really_lazy] += 1; h }
vs
counter = Hash.new 0
thingies.each do |o|
counter[o.key] += 1
end
return counter
- I use #each because it adds CLARITY. I want to enumerate each
element. I’m not folding anything.
- Yes, it’s faster. I don’t actually care about that nearly as much as
#1.
- Yes, it is more lines:
- but only if you write the inject version that way.
- I use the Weirich Method 1 of choosing {} vs do/end.
INCREASING clarity and intent.
- each line is a stand-alone concept that helps increase clarity.
Here is a perfect example of an actual folding application of #inject:
classname.split(/::/).inject(Object) { |k, n| k.const_get n }
vs:
k = Object
classname.split(/::/).each { |n| k = k.const_get n }
k
As you can see, the #inject version is incredibly clear and concise. The
second example takes longer to figure out. That is what the natural fit
of a well designed method is supposed to do.
Come to think of it (!!!) I DO have a real world example of inject that
I used in my Ruby Sadism talk:
if MODELS.keys.inject(true) {|b, klass| b and
klass.constantize.columns.map(&:name).include?
association.options[:foreign_key]} then
…
end
Have fun with that… It’s probably the most egregious use of inject
I’ve ever found. The original author actually argued that he wrote it
that way “for maintainability”.