http://weblog.jamisbuck.org/2006/10/27/mining-activesupport-object-returning
Any one else think this is worthy of standard Ruby?
Also, a cool twist on the same concept is #tap. Check out the
MenTaLgeniuS:
http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions.html
T.
One of the comments to the original article combines both methods
into a with() method that seems to be the best of both worlds.
James Edward Gray II
See also the »with construction« thread, especially as of:
Kalman
Oh yea! I remember #with from way back, but then I don't recall anyone
suggesting it return the object. Cool.
One thing though, while it covers #tap just fine, it doesn't read the
same as #returning.
T.
On 11/8/06, Trans <tran...@gmail.com> wrote:
>
> Had use for this today: #returning is a convenience method you'll find
> in both Facets and ActiveSupport. Jamis' blogged it the other day:
>
> http://weblog.jamisbuck.org/2006/10/27/mining-activesupport-object-
> returning
>
> Any one else think this is worthy of standard Ruby?
From the blog post:
>> The latter is no shorter than the former, but it reads more
>> elegantly. It feels more “Rubyish”. And there are times that it
>> can save you a few lines of code, if that’s important. (Just scan
>> the Rails source code for more examples.)
So... it usually doesn't save any lines, it adds typing, it hides the
object you're returning in the middle of a statement and it is
significantly more expensive to execute[1]. (Oh, but maybe, in some
cases, it might save you one or two lines of code.)
No thanks.
I don't think that makes it more rubyish. Every time I've
encountered #returning in Rails I've found it decreased the
readability of the code and uselessly increased the complexity.
(Wait, now what does this return? oh, yeah, way up there.)
1:
Rehearsal ---------------------------------------------
empty 0.290000 0.000000 0.290000 ( 0.391447)
returning 5.120000 0.050000 5.170000 ( 5.467861)
regular 2.660000 0.020000 2.680000 ( 2.900946)
------------------------------------ total: 8.140000sec
user system total real
empty 0.300000 0.000000 0.300000 ( 0.316623)
returning 5.160000 0.050000 5.210000 ( 5.815804)
regular 2.650000 0.020000 2.670000 ( 2.824744)
require 'benchmark'
def returning(value)
yield(value)
value
end
N = 1_000_000
Benchmark.bmbm do |bm|
bm.report 'empty' do
N.times do end
end
bm.report 'returning' do
N.times do returning(Object.new) do |o| o.nil? end end
end
bm.report 'regular' do
N.times do o = Object.new; o.nil?; o end
end
end
--
Eric Hodel - drb...@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant
Er... that seems a backwards and inconsitant interpretation. How can it
add typing if can save a line or two of code? Moreover it is optional
usage, so use it when it saves you the lines and not otherwise, or if
it simply adds to the readility, and not otherwise. As for "hides the
object you're returning in the middle of a statement", how's that? The
object is stated at the very beginning. It's very readible. It says:
here is the object we will return after doing the following things to
it. Without it you have no idea what the goal is --return statements
could be deeping embedded inside the rest of the code.
Now, I grant you the speed issue sucks --and if that's important, again
you have the option of not using it. But also, if it were built in
perhaps it could be optimized (?)
> No thanks.
>
> I don't think that makes it more rubyish. Every time I've
> encountered #returning in Rails I've found it decreased the
> readability of the code and uselessly increased the complexity.
> (Wait, now what does this return? oh, yeah, way up there.)
Well, a def can easily get "mucky". #returning can help provide some
structural constraint. Most the time it isn't needed, but on occasion I
find it helps.
Besides that the 'tap' functionality can be very hepful in it's own
right.
T.
Most of the time it adds code. Look at the two examples on the blog
post. In the returning case I add "returning do || end" in the
simple case I have an extra "=".
The example shows in the blog post shows returning saving exactly 0
lines of code. It claims that in some cases it saves lines, but
doesn't give any examples, so it is probably a very rare statement.
> Moreover it is optional usage, so use it when it saves you the
> lines and not otherwise, or if it simply adds to the readility, and
> not otherwise.
If it commonly saved lines then I'd expect the blog post would have
used such an example. I doubt that it commonly saves lines (the blog
post implies it usually does nothing to the line count).
> As for "hides the object you're returning in the middle of a
> statement", how's that? The object is stated at the very beginning.
In most every other place in ruby the last line of a code grouping
construct is the return value. This is the way I've been reading my
Ruby for years. With #returning the return value is buried in the
middle of the line between "returning" and "do".
> It's very readible. It says: here is the object we will return
> after doing the following things to it.
That's extra state I have to keep in my brain to keep the code straight.
Traditional code doesn't require me to hold extra state in my head
because its linear. Linear is simpler and simpler is more readable.
> Without it you have no idea what the goal is --return statements
> could be deeping embedded inside the rest of the code.
This statement is orthogonal to the use of #returning. You may throw
in extra returns anywhere you like regardless of the #returning block.
> Now, I grant you the speed issue sucks --and if that's important,
> again
> you have the option of not using it. But also, if it were built in
> perhaps it could be optimized (?)
Its simpler to just not have it at all. #returning gives no great
benefit and has many downsides.
>> No thanks.
>>
>> I don't think that makes it more rubyish. Every time I've
>> encountered #returning in Rails I've found it decreased the
>> readability of the code and uselessly increased the complexity.
>> (Wait, now what does this return? oh, yeah, way up there.)
>
> Well, a def can easily get "mucky". #returning can help provide some
> structural constraint. Most the time it isn't needed, but on
> occasion I
> find it helps.
#returning isn't going to add any structure since it doesn't really
do anything. You're really just replacing 'var' with 'end' and
adding a bunch of whitespace.
> Besides that the 'tap' functionality can be very hepful in it's own
> right.
So what? I didn't mention #tap in my email.
This variant saves typing and IMO is more idiomatically ruby:
class Object
def then
yield(self)
self
end
end
def foo
@foo ||= Foo.new.then do |f|
f.bar = "bar"
end
end
Otherwise you have to do this:
def foo
unless @foo
@foo = Foo.new
@foo.bar = "bar"
end
@foo
end
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
Oops, sorry. I didn't realize that #tap is the same as #then.
I can't quite see why "tap" is a good name for that, though...
I suppose it's as in "tap a keg." :)
In other words, an analogy for the Unix "tee"
utility, which is named for a T-shaped (real-life) pipe...
Or am I wrong?
Hal
> Most of the time it adds code. Look at the two examples on the blog
> post. In the returning case I add "returning do || end" in the
> simple case I have an extra "=".
> The example shows in the blog post shows returning saving exactly 0
> lines of code. It claims that in some cases it saves lines, but
> doesn't give any examples, so it is probably a very rare statement.
> If it commonly saved lines then I'd expect the blog post would have
> used such an example. I doubt that it commonly saves lines (the blog
> post implies it usually does nothing to the line count).
> In most every other place in ruby the last line of a code grouping
> construct is the return value. This is the way I've been reading my
> Ruby for years. With #returning the return value is buried in the
> middle of the line between "returning" and "do".
> That's extra state I have to keep in my brain to keep the code straight.
>
> Traditional code doesn't require me to hold extra state in my head
> because its linear. Linear is simpler and simpler is more readable.
> This statement is orthogonal to the use of #returning. You may throw
> in extra returns anywhere you like regardless of the #returning block.
> Its simpler to just not have it at all. #returning gives no great
> benefit and has many downsides.
> #returning isn't going to add any structure since it doesn't really
> do anything. You're really just replacing 'var' with 'end' and
> adding a bunch of whitespace.
Well, it does add structure, otherwise there would be not be any point
whatsoever. You may or may not like that structure and certainly you
make some fair points. But if I can ask you a question: do you use this
f = File.open('foofile')
...do stuff with f...
f.close
Or do you use
File.open('foofile') do |f|
...do stuff with f...
end
B/c all of your above arguments appear equally applicable here. The
block form of File.open provides a similar kind of structure. And while
you might say that the block form of File.open ensures the file is
closed, which gives it it's value, I say there in lies a complementary
point. A properly implemented #returning would ensure that only the
specified object is returned, and nothing else --so internal returns
would not work, because that is the structure it provides.
> > Besides that the 'tap' functionality can be very helpful in it's own
> > right.
> So what? I didn't mention #tap in my email.
B/c even if we were to agree that #returning has little value in the
context presented, both it and #tap have have the same exact
definition. Hence it has use in other contexts as well. Since
#returning has greater semantic recognition, why have #tap at all when
#returning would do as well?
BTW, is seems a bit arrogant to presume yourself the hallmark of
relevancy. I brought it up b/c I saw it as relevant. I'm sorry I did
not elaborate on the relationship. I mistakenly assumed it was plain.
T.
A tee branches. Does a keg tap branch?
Hum, maybe it does. I skipped all those I Tappa Keg parties.
More likely tap as in "tap a phone line", i.e. both siphon off a value
and let it be passed on.
martin
def foo(a)
a.each do |x|
(b ||= []) << x
end
b # => oops
end
a = [1,2,3]
p foo(a)
What you need to do, of course, is just initialize before the block:
def foo(a)
b = []
a.each do |x|
b << x
end
b
end
Ugly. Sorry, it just is. It is much more readable to use returning.
def foo(a)
returning [] do |b|
a.each do |x|
b << x
end
end
end
On another note, I don't buy the argument that this is a bad practice
because the you no longer look for the last value to see what is
returned. That's exactly what you do. The last element in this
function is the 'end' of the returning block. What does returning
return? I think its pretty obvious personally.
--
Lou.
> Ugly. Sorry, it just is. It is much more readable to use returning.
>
> def foo(a)
> returning [] do |b|
> a.each do |x|
> b << x
> end
> end
> end
I realize this is a contrived example on your part, but you did
choose it and were trying to make a relevant point. That said, I have
yet to see an example of returning used in a non-contrived way.
def foo(a)
a.map { |x| x }
end
More readable, faster, better... once again returning is used in a
completely useless way...
> def foo(a)
> a.map { |x| x }
> end
>
> More readable, faster, better...
Which is just a longer way to say:
def foo(a)
a.to_a
end
James Edward Gray II
> I realize this is a contrived example on your part, but you did
> choose it and were trying to make a relevant point. That said, I have
> yet to see an example of returning used in a non-contrived way.
[snip]
> once again returning is used in a completely useless way...
No. You're absolutely right that this was a useless, contrived
example; however, I think the point still comes across. Sometimes you
need to initialize a local variable before you use it a block. In
that case I still contend that 'returning' is more readable than the
initialize/mutate/return pattern.
This is a pattern that should be extracted; basically it's inject w/o
the iteration component.
--
Lou.
$ wc
[...]
5 12 67
> Otherwise you have to do this:
I would write it:
def foo
return @foo if @foo
@foo = Foo.new
@foo.bar = 'bar'
@foo
end
$ wc
..
6 14 77
Ten characters and one line more typing, but less punctuation. (And
those ten extra characters will be mostly be handled by my tab key.)
>> Otherwise you have to do this:
>
> I would write it:
>
> def foo
> return @foo if @foo
> @foo = Foo.new
> @foo.bar = 'bar'
> @foo
> end
>
> $ wc
> ...
> 6 14 77
>
> Ten characters and one line more typing, but less punctuation. (And those
> ten extra characters will be mostly be handled by my tab key.)
and quite easy to golf if line count matters that much
def foo
@foo ||= Foo.new.instance_eval{ self.bar = 'bar'; self }
end
-a
--
my religion is very simple. my religion is kindness. -- the dalai lama
> and quite easy to golf if line count matters that much
>
> def foo
> @foo ||= Foo.new.instance_eval{ self.bar = 'bar'; self }
> end
>
def foo; @foo ||= Foo.new.instance_eval{ self.bar = 'bar'; self } end
*smirks*
--
Lou.
But you have to type @foo _five_ times, instead of once. Oh, well. It's
a matter of taste.
ROFL
Coming full circle around the golf course... This last one above is what
lead me to prefer #then (== #tap) in the first place:
def foo
@foo ||= Foo.new.then do |f|
f.bar = "bar"
end
end
Also, #then avoids the scoping nastiness of #instance_eval.
On Fri, 17 Nov 2006, Joel VanderWerf wrote:
> Coming full circle around the golf course... This last one above is what lead
> me to prefer #then (== #tap) in the first place:
>
> def foo
> @foo ||= Foo.new.then do |f|
> f.bar = "bar"
> end
> end
>
> Also, #then avoids the scoping nastiness of #instance_eval.
I'm not getting the semantics of "then". Do you mean "then" as in, "I
took off my coat and then sat down", as opposed to then in if/then?
David
--
David A. Black | dbl...@rubypal.com
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] http://www.manning.com/black | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
Yes. Not much better than #tap, is it? Any ideas...? "and_then"?
On Fri, 17 Nov 2006, Joel VanderWerf wrote:
> dbl...@wobblini.net wrote:
>> Hi --
>>
>> On Fri, 17 Nov 2006, Joel VanderWerf wrote:
>>
>>> Coming full circle around the golf course... This last one above is what
>>> lead me to prefer #then (== #tap) in the first place:
>>>
>>> def foo
>>> @foo ||= Foo.new.then do |f|
>>> f.bar = "bar"
>>> end
>>> end
>>>
>>> Also, #then avoids the scoping nastiness of #instance_eval.
>>
>> I'm not getting the semantics of "then". Do you mean "then" as in, "I
>> took off my coat and then sat down", as opposed to then in if/then?
>
> Yes. Not much better than #tap, is it? Any ideas...? "and_then"?
"whereupon" :-) I guess it's hard to find a name that's general
enough (as opposed to, say, "post_initialize") but also expressive
enough. It does seem a slightly odd technique to me, in any case --
sort of like saying "Now I'm going to do this: this" instead of just
going to a new line and saying: "this". I'm not sure how much of the
oddness I feel is the technique as opposed to the naming issue.
Does the following technique seem odd?
class Foo
attr_accessor :a, :b, :c
def initialize
yield self if block_given?
end
end
foo = Foo.new do |f|
f.a = 1
f.b = 2
f.c = 3
end
#then is just a way of using arbitrary classes (or factories) in this
way. Maybe the name should not emphasize the temporal ("then" or
"whereupon"); so maybe "tap" is better, or "configured_using".
Btw, I don't think I've ever used #then in more than 5 or 6 places.
Usually, I just try to define classes like Foo above.
> ROFL
>
> Coming full circle around the golf course... This last one above is what lead
> me to prefer #then (== #tap) in the first place:
heh.
>
> def foo
> @foo ||= Foo.new.then do |f|
> f.bar = "bar"
> end
> end
>
> Also, #then avoids the scoping nastiness of #instance_eval.
^^^^^^^^^^^^^^^^^
indeed - good point.
> dbl...@wobblini.net wrote:
>> Hi --
>>
>> On Fri, 17 Nov 2006, Joel VanderWerf wrote:
>>
>>> Coming full circle around the golf course... This last one above is what
>>> lead me to prefer #then (== #tap) in the first place:
>>>
>>> def foo
>>> @foo ||= Foo.new.then do |f|
>>> f.bar = "bar"
>>> end
>>> end
>>>
>>> Also, #then avoids the scoping nastiness of #instance_eval.
>>
>> I'm not getting the semantics of "then". Do you mean "then" as in, "I
>> took off my coat and then sat down", as opposed to then in if/then?
>
> Yes. Not much better than #tap, is it? Any ideas...? "and_then"?
class Object
def as obj
No, just @<Tab>. I like my friendly editors.
To me, "as do" doesn't read as well as "then do".
On Fri, 17 Nov 2006, Joel VanderWerf wrote:
> Does the following technique seem odd?
>
> class Foo
> attr_accessor :a, :b, :c
> def initialize
> yield self if block_given?
> end
> end
>
> foo = Foo.new do |f|
> f.a = 1
> f.b = 2
> f.c = 3
> end
>
> #then is just a way of using arbitrary classes (or factories) in this way.
> Maybe the name should not emphasize the temporal ("then" or "whereupon"); so
> maybe "tap" is better, or "configured_using".
Part of what's odd to me about #then is that it's not
constructor-specific. Once it's defined you could do:
x = string.split(',').then do |array|
array.map! { something }
end
Bad example :-) But that's what I meant about not giving it too
specific a name, like "post_initialize", since it's not restricted to
initialization scenarios.
"then" actually works fine for me there, Englishwise. Maybe and_then
would read slightly better:
string.split(',').and_then do |array|
martin
I wonder if there's some name that embodies the fact that the object
is going to come back from this method call. The temporal ones (then,
and_then, etc.) sound kind of self-evident ("Well, of course the next
thing happens 'then'," my brain says). Perhaps that was the reasoning
behind "tap", though "tap" doesn't communicate much to me. I'm not
sure.
Good point! I thought of "pipe" but even that doesn't say that it
modifies and returns self. Maybe "apply": string.split(/\s/).apply
{|i| i.pop}.whatever. Or even "modify".
> Perhaps that was the reasoning
> behind "tap", though "tap" doesn't communicate much to me. I'm not
> sure.
'tap' had a different intent, though - "tee off the object without
disturbing it" - even if it did the same thing in the end. So you
would typically take a.foo.bar.baz.quux... and drop in a tap,
a.foo.bar.tap {|i| puts "hi mom! this is #{i}"}.baz.quux, and take
care not to modify i destructively.
martin
a.map.map.map.map
eavesdrop = Proc.new { |secret| puts secret }
a.map.map.tap(&eavesdrop) .map.map
It lets me get into the line and hear what's going on without changing
any existing behaviour. Using a proc that modifies secret is not a
good idea, just keep it for things with other types of side-effects.
There's no way to mark the object read-only for the purposes of the
passed-in proc, is there?
I haven't seen anything to change my mind about the other use. It just
seems pointless.
class Object
def tap
yield self.dup
self
end
end
Thought of that, but not everything responds to dup (nil and symbols, e.g.)
m.
Can those cases be worked around?
On Fri, 17 Nov 2006, Martin DeMello wrote:
Although... there might be cases where disturbing the object would be
desireable. For example, you could use it to work around the fact
that a lot of bang methods return nil when there's no change:
str = "abcde"
a = str.tap {|s| s.gsub!(/z/,"x") }.split(//)
I don't think dup'ing plays well with the intent, which is to do a
kind of pass-through of the object itself.
The original object -is- getting passed through... the point is to
prevent modification of it. That's what map! and each are for.
class Object
def tap
yield self.dup
self
end
end
class NilClass
def tap
yield nil
nil
end
end
class Fixnum
def tap
yield self
self
end
end
class Symbol
def tap
yield self
self
end
end
["abc", nil, 1, :foo].each { |e|
e.tap { |p|
puts p
}
}
Any other corner cases need fixing?
On Fri, 17 Nov 2006, spooq wrote:
>> I don't think dup'ing plays well with the intent, which is to do a
>> kind of pass-through of the object itself.
>
> The original object -is- getting passed through... the point is to
> prevent modification of it. That's what map! and each are for.
I see what you mean. Well, as per my gsub! example, I think modifying
it can be useful :-)
This can be rewritten using existing keywords tho... if you want to
actually use the original, use each and gsub!, or if you want to use
the output, then use map and gsub. Doing both at the same time just
means that your nice single chain of operations will either
a) end up giving you the same end result as your input is now modified
to be, which you have to admit is not very useful
or
b) somewhere else along the chain you'll stop modifying one, which is
going to be hideously confusing.
If the original and the output share steps, make that obvious.
well, #with is the other common synonym. but #returning seems as well.
you just read it with the object of returning being stated first, which
we sometimes do in english.
def foo
@foo ||= Foo.new.returning do |f|
f.bar = "bar"
end
end
T.
"Do as if frozen". Perhaps #freeze could take a block?
T.
Nice. Sounds very reusable.