Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

#returning and #tap

1 view
Skip to first unread message

Trans

unread,
Nov 8, 2006, 9:31:19 AM11/8/06
to
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?

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.

James Edward Gray II

unread,
Nov 8, 2006, 9:53:41 AM11/8/06
to

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

Kalman Noel

unread,
Nov 8, 2006, 10:01:35 AM11/8/06
to
Trans wrote:
[about #returning and #tap]

See also the »with construction« thread, especially as of:

45426320...@path.berkeley.edu

Kalman

spooq

unread,
Nov 8, 2006, 11:01:31 AM11/8/06
to
MenTaLguY's use cases seem the more convincing of the two. Why would I
care about keeping an object anonymous when I'm leaving that scope
anyway? I guess in an inline lambda function it might be appropriate.
'tap' is what it's likely to be used for most of the time. I'd call it
'k', personally, maybe with an alias to 'tap' or 'tee'. I would like
to see it in standard Ruby.

Trans

unread,
Nov 8, 2006, 11:03:01 AM11/8/06
to

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.

spooq

unread,
Nov 8, 2006, 11:23:58 AM11/8/06
to
Just realised that of course tap != k :P


On 11/8/06, Trans <tran...@gmail.com> wrote:
>

Eric Hodel

unread,
Nov 15, 2006, 4:49:30 PM11/15/06
to
On Nov 8, 2006, at 6:40 AM, Trans 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

http://trackmap.robotcoop.com

Trans

unread,
Nov 15, 2006, 5:49:52 PM11/15/06
to

Eric Hodel wrote:
> On Nov 8, 2006, at 6:40 AM, Trans 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.)

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.

Eric Hodel

unread,
Nov 15, 2006, 6:32:32 PM11/15/06
to
On Nov 15, 2006, at 2:50 PM, Trans wrote:
> Eric Hodel wrote:
>> On Nov 8, 2006, at 6:40 AM, Trans 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.)
>
> Er... that seems a backwards and inconsitant interpretation. How
> can it
> add typing if can save a line or two of code?

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.

Joel VanderWerf

unread,
Nov 15, 2006, 8:40:43 PM11/15/06
to
Eric Hodel wrote:
..

> 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.)

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

Joel VanderWerf

unread,
Nov 15, 2006, 8:45:02 PM11/15/06
to
Joel VanderWerf wrote:
> This variant saves typing and IMO is more idiomatically ruby:
>
> class Object
> def then
> yield(self)
> self
> end
> end

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...

Hal Fulton

unread,
Nov 15, 2006, 9:08:35 PM11/15/06
to
Joel VanderWerf wrote:
> Joel VanderWerf wrote:
>
>> This variant saves typing and IMO is more idiomatically ruby:
>>
>> class Object
>> def then
>> yield(self)
>> self
>> end
>> end
>
>
> 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

Trans

unread,
Nov 15, 2006, 9:32:41 PM11/15/06
to

Eric Hodel wrote:

> 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.

Joel VanderWerf

unread,
Nov 15, 2006, 11:11:16 PM11/15/06
to
Hal Fulton wrote:
> Joel VanderWerf wrote:
..

>> 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...

A tee branches. Does a keg tap branch?

Hum, maybe it does. I skipped all those I Tappa Keg parties.

Martin DeMello

unread,
Nov 16, 2006, 2:39:21 AM11/16/06
to
On 11/16/06, Hal Fulton <hal...@hypermetrics.com> wrote:

> Joel VanderWerf wrote:
> >
> > 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?

More likely tap as in "tap a phone line", i.e. both siphon off a value
and let it be passed on.

martin

Louis J Scoras

unread,
Nov 16, 2006, 10:46:08 AM11/16/06
to
I think where returning really makes a difference is with local
variables. If you have some code where you want to initialize a local
variable in a block and then use it afterward your up the creek.

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.

Ryan Davis

unread,
Nov 16, 2006, 11:56:56 AM11/16/06
to

On Nov 16, 2006, at 10:46 AM, Louis J Scoras wrote:

> 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...


James Edward Gray II

unread,
Nov 16, 2006, 12:04:18 PM11/16/06
to
On Nov 16, 2006, at 10:56 AM, Ryan Davis wrote:

> 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

Louis J Scoras

unread,
Nov 16, 2006, 1:01:27 PM11/16/06
to
On 11/16/06, Ryan Davis <ryand...@zenspider.com> wrote:

> 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.

Eric Hodel

unread,
Nov 16, 2006, 3:04:30 PM11/16/06
to
On Nov 15, 2006, at 5:40 PM, Joel VanderWerf wrote:
> Eric Hodel wrote:
> ...

>> 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.)
>
> 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

$ 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.)

ara.t....@noaa.gov

unread,
Nov 16, 2006, 3:38:41 PM11/16/06
to
On Fri, 17 Nov 2006, Eric Hodel wrote:

>> 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

Louis J Scoras

unread,
Nov 16, 2006, 4:08:55 PM11/16/06
to

> 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.

Joel VanderWerf

unread,
Nov 16, 2006, 4:57:52 PM11/16/06
to
> ...

> 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.)

But you have to type @foo _five_ times, instead of once. Oh, well. It's
a matter of taste.

Joel VanderWerf

unread,
Nov 16, 2006, 4:59:42 PM11/16/06
to
ara.t....@noaa.gov wrote:
> On Fri, 17 Nov 2006, Eric Hodel wrote:
>
>>> 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

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.

dbl...@wobblini.net

unread,
Nov 16, 2006, 5:08:16 PM11/16/06
to
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?


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

Joel VanderWerf

unread,
Nov 16, 2006, 5:12:34 PM11/16/06
to
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"?

dbl...@wobblini.net

unread,
Nov 16, 2006, 5:24:45 PM11/16/06
to
Hi --

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.

Joel VanderWerf

unread,
Nov 16, 2006, 5:38:11 PM11/16/06
to
dbl...@wobblini.net wrote:
> Hi --
>
> 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.

ara.t....@noaa.gov

unread,
Nov 16, 2006, 5:41:29 PM11/16/06
to
On Fri, 17 Nov 2006, Joel VanderWerf wrote:

> 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.

ara.t....@noaa.gov

unread,
Nov 16, 2006, 5:44:18 PM11/16/06
to
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"?

class Object
def as obj

Eric Hodel

unread,
Nov 16, 2006, 5:55:36 PM11/16/06
to

No, just @<Tab>. I like my friendly editors.

Joel VanderWerf

unread,
Nov 16, 2006, 6:10:33 PM11/16/06
to
ara.t....@noaa.gov wrote:
> 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"?
>
> class Object
> def as obj
> end

To me, "as do" doesn't read as well as "then do".

dbl...@wobblini.net

unread,
Nov 16, 2006, 6:20:12 PM11/16/06
to
Hi --

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.

Martin DeMello

unread,
Nov 17, 2006, 3:37:30 AM11/17/06
to
On 11/17/06, dbl...@wobblini.net <dbl...@wobblini.net> wrote:
>
> 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

"then" actually works fine for me there, Englishwise. Maybe and_then
would read slightly better:

string.split(',').and_then do |array|

martin

dbl...@wobblini.net

unread,
Nov 17, 2006, 6:01:57 AM11/17/06
to
Hi --

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.

Martin DeMello

unread,
Nov 17, 2006, 6:21:10 AM11/17/06
to
On 11/17/06, dbl...@wobblini.net <dbl...@wobblini.net> wrote:
>
> 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).

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

spooq

unread,
Nov 17, 2006, 6:37:24 AM11/17/06
to
I definitely think of it as tapping a phone line.

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.

spooq

unread,
Nov 17, 2006, 6:45:21 AM11/17/06
to
Actually, how about giving the proc a copy of the object, rather than
the real deal?

class Object
def tap
yield self.dup
self
end
end

Martin DeMello

unread,
Nov 17, 2006, 6:49:01 AM11/17/06
to
On 11/17/06, spooq <spo...@gmail.com> wrote:
> Actually, how about giving the proc a copy of the object, rather than
> the real deal?
>
> 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.

spooq

unread,
Nov 17, 2006, 6:53:22 AM11/17/06
to

Can those cases be worked around?

dbl...@wobblini.net

unread,
Nov 17, 2006, 6:57:58 AM11/17/06
to
Hi --

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(//)

dbl...@wobblini.net

unread,
Nov 17, 2006, 6:58:59 AM11/17/06
to
Hi --

I don't think dup'ing plays well with the intent, which is to do a
kind of pass-through of the object itself.

spooq

unread,
Nov 17, 2006, 7:05:07 AM11/17/06
to
> 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?

dbl...@wobblini.net

unread,
Nov 17, 2006, 7:06:57 AM11/17/06
to
Hi --

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 :-)

spooq

unread,
Nov 17, 2006, 7:24:40 AM11/17/06
to
> Hi --
>
> 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.

Trans

unread,
Nov 17, 2006, 7:48:15 AM11/17/06
to

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.

Trans

unread,
Nov 17, 2006, 7:56:48 AM11/17/06
to

spooq wrote:
> Actually, how about giving the proc a copy of the object, rather than
> the real deal?
>
> class Object
> def tap
> yield self.dup
> self
> end
> end

"Do as if frozen". Perhaps #freeze could take a block?

T.

spooq

unread,
Nov 17, 2006, 8:06:42 AM11/17/06
to

Nice. Sounds very reusable.

Ross Bamford

unread,
Nov 17, 2006, 8:25:03 AM11/17/06