[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

alias_method :tap, :affect

Josh Susser

11/12/2007 5:58:00 PM


Ruby's enumeration message names come directly from its Smalltalk
ancestry. There are also some aliases. Like so:

*method* *alias*
collect map
detect find
select find_all
reject
inject

Ruby 1.9 introduces the K-combinator in the form of the method
Object#tap. I don't get the name #tap at all. So I'm proposing a new
name/alias pair, with a more meaningful name:

*method* *alias*
affect tap

"foo".affect { |s| s << "bar" }
#=> "foobar"

The K-combinator is all about side-effecting the receiver object, so
"affect" seems like a meaningful name for that functionality (whereas
"effect" would be wrong!). "tap" is still cute, and rhymes with "map",
so having both seems like a good Rubyish way to go.

Thoughts?

--
Josh Susser
http://blog.hasmanyt...
--
Posted via http://www.ruby-....

47 Answers

Rick DeNatale

11/12/2007 6:39:00 PM

0

On Nov 12, 2007 12:58 PM, Josh Susser <josh@hasmanythrough.com> wrote:
>
> Ruby's enumeration message names come directly from its Smalltalk
> ancestry. There are also some aliases. Like so:
>
> *method* *alias*
> collect map
> detect find
> select find_all
> reject
> inject
>
> Ruby 1.9 introduces the K-combinator in the form of the method
> Object#tap. I don't get the name #tap at all. So I'm proposing a new
> name/alias pair, with a more meaningful name:
>
> *method* *alias*
> affect tap
>
> "foo".affect { |s| s << "bar" }
> #=> "foobar"
>
> The K-combinator is all about side-effecting the receiver object, so
> "affect" seems like a meaningful name for that functionality (whereas
> "effect" would be wrong!). "tap" is still cute, and rhymes with "map",
> so having both seems like a good Rubyish way to go.

Of course, active-support also defines the same method as Object#returning

I'm not sure how the Smalltalk intro ties in here. I can't recall a
method implementation of the k-combinator in common use in Smalltalk,
in most cases the message cascade syntax ending with an invocation of
#yourself, would serve the cases where the k-combinator is used:

Ruby

a = MyObject.new.tap do |o| # or returning, or affect
o.foo = 1
o.bar = "whatever"
end

Smalltalk
a = MyObject.new foo: 1;
bar: "whatever";
yourself

For Rubyists unfamiliar with Smalltalk, the message cascade triggered
by the ; meant send the message after the ; to the receiver of the
last message. Object#yourself was defined to return the receiver, and
was not typically overridden.

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Josh Susser

11/12/2007 7:04:00 PM

0

Rick Denatale wrote:
> On Nov 12, 2007 12:58 PM, Josh Susser <josh@hasmanythrough.com> wrote:
>>
>> The K-combinator is all about side-effecting the receiver object, so
>> "affect" seems like a meaningful name for that functionality (whereas
>> "effect" would be wrong!). "tap" is still cute, and rhymes with "map",
>> so having both seems like a good Rubyish way to go.
>
> Of course, active-support also defines the same method as
> Object#returning

Not exatcly. #tap works on the receiver, #returning works on an
argument. #tap is more object-oriented IMO.

returning("foo") { |s| s << "bar" }
"foo".affect { |s| s << "bar" }

> I'm not sure how the Smalltalk intro ties in here.

It's just to say how awsome methods that end in "ect" are, and why :-)

> I can't recall a
> method implementation of the k-combinator in common use in Smalltalk,
> in most cases the message cascade syntax ending with an invocation of
> #yourself, would serve the cases where the k-combinator is used:
>
> Ruby
>
> a = MyObject.new.tap do |o| # or returning, or affect
> o.foo = 1
> o.bar = "whatever"
> end
>
> Smalltalk
> a = MyObject.new foo: 1;
> bar: "whatever";
> yourself
>
> For Rubyists unfamiliar with Smalltalk, the message cascade triggered
> by the ; meant send the message after the ; to the receiver of the
> last message. Object#yourself was defined to return the receiver, and
> was not typically overridden.

That's exactly right. Cascaded messages were cool, and came right out of
the stack machine nature of Smalltalk's bytecodes. And I've often longed
for a #yourself method in Ruby, but usually only to get to the target of
a proxy object.

> --
> Rick DeNatale
>
> My blog on Ruby
> http://talklikeaduck.denh...

--
Josh Susser
http://blog.hasmanyt...

--
Posted via http://www.ruby-....

furtive.clown

11/12/2007 7:09:00 PM

0


This is the first I've heard of Object#tap. It seems backward because
you almost always want the result of the block, not the object back
again. All of my ruby scripts use

class Object
def as
yield self
end
end

E.g, a recent script of mine does

platform_audio_files = audio_stems.map { |f|
f + "." + audio_ext[platform]
}.as { |t|
t + audio_basenames
}.map { |f|
File.join(audio_dir, f)
}

I know this could be written

platform_audio_files = (audio_stems.map { |f|
f + "." + audio_ext[platform]
} + audio_basenames).map { |f|
File.join(audio_dir, f)
}

but I find that uncouth compared to the former.

If ruby is adding anything, it should be the behavior of Object#as,
not Object#tap.





ara.t.howard

11/12/2007 7:18:00 PM

0


On Nov 12, 2007, at 11:38 AM, Rick DeNatale wrote:

> a = MyObject.new.tap do |o| # or returning, or affect
> o.foo = 1
> o.bar = "whatever"
> end

just as easy to do this though:

a = MyObject.new.instance_eval{
foo 1
bar 'whatever'
self
}

no special methods needed...

returning is, imho, over rated.

cheers.

a @ http://codeforp...
--
it is not enough to be compassionate. you must act.
h.h. the 14th dalai lama




furtive.clown

11/12/2007 7:22:00 PM

0

It would seem Object#tap is for the imperative style and Object#as is
for the functional style. My ruby code is always improved ---
clearer, smaller, more elegant --- when I use a functional style as
much as possible. For example cutting down on side-effects and being
in the mindset of chaining results rather than re-assigning to the
same variable. Please, if you add Object#tap then you must add its
functional equivalent Object#as (call it what you wish).

Martin DeMello

11/12/2007 7:32:00 PM

0

On Nov 12, 2007 11:10 AM, <furtive.clown@gmail.com> wrote:
>
> This is the first I've heard of Object#tap. It seems backward because
> you almost always want the result of the block, not the object back
> again.

#tap is the opposite use case - you want to "tap" the object stream,
observing without affecting. It's very handy for debugging, where you
can tap into a chain of method calls, print something out and continue
on your way, e.g.

foo.map(&:bar).tap {|a| p a}.inject...

martin

Yossef Mendelssohn

11/12/2007 7:42:00 PM

0

On Nov 12, 11:58 am, Josh Susser <j...@hasmanythrough.com> wrote:
> Ruby's enumeration message names come directly from its Smalltalk
> ancestry. There are also some aliases. Like so:
>
> *method* *alias*
> collect map
> detect find
> select find_all
> reject
> inject
>
> Ruby 1.9 introduces the K-combinator in the form of the method
> Object#tap. I don't get the name #tap at all. So I'm proposing a new
> name/alias pair, with a more meaningful name:
>
> *method* *alias*
> affect tap
>
> "foo".affect { |s| s << "bar" }
> #=> "foobar"
>
> The K-combinator is all about side-effecting the receiver object, so
> "affect" seems like a meaningful name for that functionality (whereas
> "effect" would be wrong!). "tap" is still cute, and rhymes with "map",
> so having both seems like a good Rubyish way to go.
>
> Thoughts?
>
> --
> Josh Susserhttp://blog.hasmanyt...

I remember some of this talk from RubyConf IRC, and I'm still not
convinced about "affect". Personally, I would expect a method called
"affect" to return the receiver with some change, whereas tap returns
the receiver as-is. Your example modifies the receiver in place, so
it's a little confounding.

MenTaLguY has yet to chime in, so I'll beat him to the punch with his
own link. http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-e...
has some examples of what Object#tap is (possibly) intended for, and
that makes the name perfectly sensible, as far as I'm concerned. It's
for "tapping into" the line of calls, allowing you to see what's going
on at that point, but not changing anything.

Don't get me wrong, though. I'm all for more methods that end in
"ect".

I also find tap and returning to be for completely different purposes.
The point of the latter is mainly to get away from things like

def some_method
var = ''
some_stuff_with(var)
var += something_else
var.more_things
var
end

and instead let you write something like

def some_method
var = ''
returning(var) do |x|
some_stuff_with(x)
x += something_else
x.more_things
end
end

--
-yossef
> --
> Posted viahttp://www.ruby-....


Josh Susser

11/12/2007 7:44:00 PM

0

Martin DeMello wrote:
> On Nov 12, 2007 11:10 AM, <furtive.clown@gmail.com> wrote:
>>
>> This is the first I've heard of Object#tap. It seems backward because
>> you almost always want the result of the block, not the object back
>> again.
>
> #tap is the opposite use case - you want to "tap" the object stream,
> observing without affecting. It's very handy for debugging, where you
> can tap into a chain of method calls, print something out and continue
> on your way, e.g.
>
> foo.map(&:bar).tap {|a| p a}.inject...
>
> martin

That's a great example of a use-case for #tap. (I think I remember that
from the original discussion of the feature.) But the K-combinator style
side-effect is valid too. Just look at all the uses of #returning in
Rails.

--
Posted via http://www.ruby-....

furtive.clown

11/12/2007 8:38:00 PM

0

On Nov 12, 2:31 pm, Martin DeMello <martindeme...@gmail.com> wrote:
> On Nov 12, 2007 11:10 AM, <furtive.cl...@gmail.com> wrote:
>
>
>
> > This is the first I've heard of Object#tap. It seems backward because
> > you almost always want the result of the block, not the object back
> > again.
>
> #tap is the opposite use case - you want to "tap" the object stream,
> observing without affecting.

OK I understand Object#tap now. At first I thought the motivation was
to modify the object inside the block, but now I see that it can be a
useful part of functional-style ruby.

So, to throw this out again --- there should also be Object#as which
returns the block result (I like the name 'as' but I am not
particularly attached to it). I can personally attest to its
usefulness, and I can show reams of examples in addition to the above
one I gave.


James Gray

11/12/2007 8:56:00 PM

0

On Nov 12, 2007, at 2:40 PM, furtive.clown@gmail.com wrote:

> So, to throw this out again --- there should also be Object#as which
> returns the block result (I like the name 'as' but I am not
> particularly attached to it). I can personally attest to its
> usefulness, and I can show reams of examples in addition to the above
> one I gave.

I guess I want to see some more examples, because I'm seriously
doubting the usefulness. The example you showed so far was using it
to create this:

platform_audio_files = audio_stems.map { |f|
f + "." + audio_ext[platform]
}.as { |t|
t + audio_basenames
}.map { |f|
File.join(audio_dir, f)
}

instead of:

platform_audio_files = (audio_stems.map { |f|
f + "." + audio_ext[platform]
} + audio_basenames).map { |f|
File.join(audio_dir, f)
}

I don't really understand what the problem is with the second version
that's shorter and easier for me to understand. If it's the need to
introduce some parentheses into the call chain, we can fix that:

platform_audio_files = audio_stems.map { |f|
f + "." + audio_ext[platform]
}.concat(audio_basenames).map { |f|
File.join(audio_dir, f)
}

So yes, please send more examples as I remain unconvinced.

James Edward Gray II