On Nov 16, 2006, at 1:57 PM, Joel VanderWerf wrote:
Eric Hodel wrote:
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.)
But you have to type @foo _five_ times, instead of once. Oh, well. It's a matter of taste.
--
Eric Hodel - drbrain@segment7.net - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant
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.
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...
···
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.
[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.
···
On 11/16/06, Ryan Davis <ryand-ruby@zenspider.com> 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.
> Hi --
>
>
>> 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"?
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.
···
dblack@wobblini.net wrote:
> 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.
···
On Fri, 17 Nov 2006, Joel VanderWerf wrote:
dblack@wobblini.net wrote:
On Fri, 17 Nov 2006, Joel VanderWerf wrote:
--
vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407
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.
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|
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.
David
···
On Fri, 17 Nov 2006, Martin DeMello wrote:
On 11/17/06, dblack@wobblini.net <dblack@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
···
On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:
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.
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
···
On 11/17/06, spooq <spoooq@gmail.com> wrote:
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.
On 11/17/06, dblack@wobblini.net <dblack@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.
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(//)