Object#returning (Ruby's K Combinator)
weblog.jamisbuck.org
weblog.jamisbuck.org
merb is one of the best-written Ruby libraries I've seen, in large part because they're focused on avoiding clever code.
Instead, I note that neither of these things are part of the standard library today. Which means, you must open the Object or Symbol classes to implement them. Nothing wrong with that in your code, but when you do so in a framework like merb (or Rails!), everyone using your framework gets your little patches in their namespace as well.
My feeling is that with the way Ruby's global class namespace works today, opening a core class has much more serious consequences when you are writing a framework, gem, or plug-in then when you are writing code that does not have "downstream clients."
So the real-world experience for Smalltalk is that the former is mostly FUD. The latter is a real issue. I suspect that this pretty much applies to the other dynamic languages as well.
I also completely and totally disagree that they're not clever. There is nothing _not_ clever about #returning. It is second only to using inject because you're too lazy to assign to a variable first. Nevermind that every railz0r that does that ignores the fact that they've now assigned N times instead of once.
Clever code, in my mind, is a form of hidden cost. It seems to be what I get paid to deal with (read: remove) the most.
def squidget(options = {})
squid = { :foo => :bar, ... }.merge(options)
...
squid[:bash] = :blitz
end
The method was returning :blitz instead of the hash that the method was constructing. I changed it to a returning statement and to my eye it looks much more clear what is going on.Of course, you could add a "return squid" at the end, or just plain "squid," but somehow that seems less clear, since when you start manufacturing the squid, it isn't obvious what you are doing until you reach the end of the method. Witha returning statement, you know right away.
All in all, I feel good about all constructions in Ruby that use blocks to wrap around the stuff they are doing, like
Foo.transaction do
...
end
But you're younger than I am, so your brain is probably sharper. Maybe you find it easier to just work directly with imperitive-style code. Many people I know prefer it that way. I know I like to simulate Scheme as much as possible, and use expressions instead of assigning local variables as much as possible. It could be laziness on my part.Besides, I'm not that much younger than you. :P
I think it is mostly that you went the lispy route while I took the smalltalk route.
ETA:
actually... I'll go one step further...
`returning` violates "Do the Simplest Thing That Could Possibly Work" flat out without giving you anything in return.
We agree on the maxim while disagreeing with how to apply it. For me, "returning" is simpler because it seems to involve fewer moving parts: you don't have to remember to write 'squid' at the end and you get a local variable that is scoped only to the block where it is needed.
To you, it is a gross and needlessly complicated way to write begin; squid = ...; squid; end. I can see how you would feel that way without feeling that way myself.
I'm curious about the aversion to Symbol#to_proc. If you see [...].map(&:name) and think of it as an abbreviation for { |element| element.name }, there isn't much to like.
But coming from Smalltalk, does your brain ever click over and make you think of [...].map(&:name) as meaning "map the message :name over this array"?
I ask because when I think of languages like Smalltalk and Ruby, I go along with Alan kay and thenk of them as being message-oriented rather than object-oriented. Symbol#to_proc seems to fit that somehow in my brain. I dunno how to explain it any other way, it really feels less like an abbreviation to save some characters and more like getting closer to what I am trying to say...
returning (Fubar.new) do |f|
...
end
Your write: Fubar.new.tap do |f|
...
end
Shameless plug-whoring: The andand gem includes an enhanced Object#tap: http://andand.rubyforge.org foo = returning(Object.new) do |f|
def f.bar
...
end
def f.bash
...
end
end foo = returning(Object.new) do |f|
def f.bar
...
end
def f.bash
...
end
end
# vs
foo = Object.new
def foo.bar
...
end
def foo.bash
...
end
1 line shorter, less indentation, more clear. What does returning get you besides another message send and block activation? I have yet to see a good use for it. (yes, I'm biased. calibrate accordingly)For the same reason, I write:
foo = bar
bash = blitz
When teh two statements are independant, and: foo, bar = bash, blitz
When they are deeply related and should not be broken up. Sometimes that is hard to read and I will get gratuitous: begin
foo = bar
bash = blitz
end
What can I say... that uses more characters than needed. It isn't always about saving electrons for me.