Forum: Ruby alias_method :tap, :affect

9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2007-11-12 18:57
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.hasmanythrough.com
8f6f95c4bd64d5f10dfddfdcd03c19d6?d=identicon&s=25 Rick Denatale (rdenatale)
on 2007-11-12 19:40
(Received via mailing list)
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

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.denhaven2.com/
9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2007-11-12 20:03
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.denhaven2.com/

--
Josh Susser
http://blog.hasmanythrough.com
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 20:10
(Received via mailing list)
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.
1bac2e65d64faf472cf2ebc94f0f5ee0?d=identicon&s=25 ara.t.howard (Guest)
on 2007-11-12 20:18
(Received via mailing list)
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://codeforpeople.com/
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 20:26
(Received via mailing list)
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).
Ae16cb4f6d78e485b04ce1e821592ae5?d=identicon&s=25 Martin DeMello (Guest)
on 2007-11-12 20:32
(Received via mailing list)
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
B1b1d33e0655e841d4fd8467359c58d0?d=identicon&s=25 Yossef Mendelssohn (Guest)
on 2007-11-12 20:42
(Received via mailing list)
On Nov 12, 11:58 am, Josh Susser <j...@hasmanythrough.com> wrote:
> Ruby 1.9 introduces the K-combinator in the form of the method
> "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.hasmanythrough.com

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/...
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
9f0f89bbd9e1ecfbaab6584e429b7a2f?d=identicon&s=25 Josh Susser (jsusser)
on 2007-11-12 20:44
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.
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 21:41
(Received via mailing list)
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.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-12 21:58
(Received via mailing list)
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
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-11-12 22:07
(Received via mailing list)
On Nov 12, 2007 8:10 PM,  <furtive.clown@gmail.com> wrote:
>
I am so glad, I really needed Object#itself in Labrador, somehow I
felt alone with this approach, now it seems i am not that weird after
all.

I however adhere with Ara's POV, what is there that instance_eval
cannot give you?

Cheers
Robert
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2007-11-12 22:16
(Received via mailing list)
ara.t.howard wrote:
> a = MyObject.new.instance_eval{
>   foo 1
>   bar 'whatever'
>   self
> }
>
> no special methods needed...
>
> returning is, imho, over rated.

Really?

There is the scope issue. I use this construct sometimes:

   def socket
     @socket ||= UDPSocket.open.then do |s|
       s.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
       s.connect(@svr_addr, @svr_port)
     end
   end

   def initialize
     @svr_addr, @svr_port = ...
   end

   def run
     loop { socket.send(...) }
   end

#instance_eval makes it unnatural to access instance variables of the
outer scope (you have to pass them through locals).

(#then is my own personal preference to #tap, which usually makes me
start thinking about Spinal Tap, and I have to laugh for a while.)
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 22:27
(Received via mailing list)
On Nov 12, 3:55 pm, James Edward Gray II <ja...@grayproductions.net>
wrote:
>     }.map { |f|
>
> So yes, please send more examples as I remain unconvinced.
It was only a coincidence that you found an Array method which
corresponded to the operation in the block, namely Array#concat.  Your
example breaks down once the operation is nontrivial --- something
more complex than '+'.

We can also introduce temporaries inside the block without causing
distraction in the outer scope.  The purpose of the block chain is to
produce a value for platform_audio_files; a flood temporary variables
on the same scope obscures this purpose.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-12 22:46
(Received via mailing list)
On Nov 12, 2007, at 3:25 PM, furtive.clown@gmail.com wrote:

>>     }.as { |t|
>>        File.join(audio_dir, f)
>>     }
> produce a value for platform_audio_files; a flood temporary variables
> on the same scope obscures this purpose.

My counter argument would be that, if it becomes non-trivial, you're
trying to be too fancy doing it all in one call chain.  Split it up
into the few lines of code it needs to be.  Then, if the variables
and such become significant enough to affect the outer scope, it's
time to tuck the operation away in some method where it belongs.

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 22:55
(Received via mailing list)
On Nov 12, 4:06 pm, Robert Dober <robert.do...@gmail.com> wrote:
> >    end
>
> I am so glad, I really needed Object#itself in Labrador, somehow I
> felt alone with this approach, now it seems i am not that weird after
> all.
>
> I however adhere with Ara's POV, what is there that instance_eval
> cannot give you?

Well, I don't want a block which is evaluated in the instance's
context.  Why would I even risk doing that?  What if I want to use the
"self" before the instance_eval?  What if I use "self" inside the
block while forgetting that I'm inside an instance_eval?  I'd be
screwed for no reason.

The use instance_eval communicates a specific purpose which entirely
different from the purpose of Object#as.  I want to take the current
object, name it *as* something, perform some operations on it, and
give the result.

The current object should not be given the name "self", which is a
special name.  It should be given a temporary name (e.g. "t") which
communicates its temporal non-specialness.  Object#instance_eval is
the former, Object#as is the latter.
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 23:20
(Received via mailing list)
On Nov 12, 4:37 pm, James Edward Gray II <ja...@grayproductions.net>
wrote:
> >> to create this:
>
> >>     platform_audio_files = audio_stems.map { |f|
> > more complex than '+'.
> time to tuck the operation away in some method where it belongs.
If you had only one temporary variable in the block, would you move it
to a method?  I would prefer to keep that temporary inside the #as
block -- it truly improves understandably and readability.  There is
no confusion about what the intent is: we are interested in the
result, not the temporaries.

#as is also used to make function calls prettier:

filename = File.basename(input.map { |t|
   t.gsub(re, "_")
}.join)

filename = input.map { |t|
   t.gsub(re, "_")
}.join.as { |t|
   File.basename(t)
}

I greatly prefer the latter.  I want to chain, chain, chain, and I
don't want pesky prefix-y function calls like File.basename() to cramp
my style.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-12 23:26
(Received via mailing list)
On Nov 12, 2007, at 4:20 PM, furtive.clown@gmail.com wrote:

> }
>
> I greatly prefer the latter.

I would use:

   file_name = File.basename(input.join.gsub(re, "_"))

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-12 23:35
(Received via mailing list)
On Nov 12, 5:25 pm, James Edward Gray II <ja...@grayproductions.net>
wrote:
> > }.join.as { |t|
> >    File.basename(t)
> > }
>
> > I greatly prefer the latter.
>
> I would use:
>
>    file_name = File.basename(input.join.gsub(re, "_"))
>

Not if re has \A or \Z.  In any case you may be missing the point,
though.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-12 23:45
(Received via mailing list)
On Nov 12, 2007, at 4:35 PM, furtive.clown@gmail.com wrote:

>>> filename = input.map { |t|
>>
>
> Not if re has \A or \Z.

If the Regexp contain such an anchor, we probably shouldn't be
calling gsub().

> In any case you may be missing the point, though.

I may be, sure.  As may you.  ;)

My point is that your examples aren't convincing me.  Even if we have
to support the anchor, I prefer your first example.  It's more
obvious to me.  But I'll sleep OK tonight if we just agree to
disagree on that.

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 00:30
(Received via mailing list)
>
> >>    file_name = File.basename(input.join.gsub(re, "_"))
>
> > Not if re has \A or \Z.
>
> If the Regexp contain such an anchor, we probably shouldn't be
> calling gsub().

We don't know what's in re, so we can't make a judgment about gsub.

> My point is that your examples aren't convincing me.  Even if we have
> to support the anchor, I prefer your first example.  It's more
> obvious to me.  But I'll sleep OK tonight if we just agree to
> disagree on that.

Well I've used this for several years, and the examples I gave are
simplistic but illustrative.  The purpose was to understand the
general idea from the particular examples, even though they are
simplistic.

I've already suggested a less trivial example:

    files = stems.map { |f|
       f + "." + ext[platform]
    }.as { |t|
       z = transform1(t)
       transform2(basenames + transform3(t, z))
    }.map { |f|
       File.join(audio_dir, f)
    }

Your suggestion was to put the middle block into a method.  While that
may be best in certain circumstances, there is still a niche for small-
but-not-trivial cases where keeping the block may be clearer.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-13 00:40
(Received via mailing list)
On Nov 12, 2007, at 5:30 PM, furtive.clown@gmail.com wrote:

>>
>>>>    file_name = File.basename(input.join.gsub(re, "_"))
>>
>>> Not if re has \A or \Z.
>>
>> If the Regexp contain such an anchor, we probably shouldn't be
>> calling gsub().
>
> We don't know what's in re, so we can't make a judgment about gsub.

If you don't know the contents of the Regexp and thus you call gsub
(), you are a much braver programmer than I am!

It's clear we just disagree and I've said my peace now.  You win.  ;)

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 00:55
(Received via mailing list)
On Nov 12, 6:39 pm, James Edward Gray II <ja...@grayproductions.net>
wrote:
>
> If you don't know the contents of the Regexp and thus you call gsub
> (), you are a much braver programmer than I am!

You've never used a Regexp variable?  Always a Regexp literal for
you?  That's why I put a variable there, to make the point that it
can't just be concatenated.

> It's clear we just disagree and I've said my peace now.  You win.  ;)

In my previous post, I did however make a valid argument with a valid
example, to which you did not respond.
1ebbd2ca9df3016f6c4f685bc75fe66d?d=identicon&s=25 Raul Parolari (raulparolari)
on 2007-11-13 01:31
unknown wrote:
> On Nov 12, 6:39 pm, James Edward Gray II <ja...@grayproductions.net>
> wrote:
>>
>> If you don't know the contents of the Regexp and thus you call gsub
>> (), you are a much braver programmer than I am!
>
> You've never used a Regexp variable?  Always a Regexp literal for
> you?  That's why I put a variable there, to make the point that it
> can't just be concatenated.
>
>> It's clear we just disagree and I've said my peace now.  You win.  ;)
>
> In my previous post, I did however make a valid argument with a valid
> example, to which you did not respond.


The exchange was great from both sides.

But the point that JGII is making about 're' is not that he prefers
Regexp literals vs variables! it is that if the regexp variable
contained an anchor like \A it does not make a lot of sense to call
gsub... because it would at most match once!

So while we may not know the exact content of the regexp (eg, it is
provided by another component, etc), it makes sense to think that at
least we know it is suited to a global replace (if not, what exactly are
we doing? the whole thing loses any sense).
1bac2e65d64faf472cf2ebc94f0f5ee0?d=identicon&s=25 ara.t.howard (Guest)
on 2007-11-13 03:43
(Received via mailing list)
On Nov 12, 2007, at 2:15 PM, Joel VanderWerf wrote:

> Really?
>
> There is the scope issue. I use this construct sometimes:

quite true.  still, i personally think such a cozy method as tap/
whatever *should* be in the instance scope - otherwise it is just eye
candy.

cheers.

a @ http://codeforpeople.com/
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 07:20
(Received via mailing list)
On Nov 12, 7:31 pm, Raul Parolari <raulparol...@gmail.com> wrote:
>
> But the point that JGII is making about 're' is not that he prefers
> Regexp literals vs variables! it is that if the regexp variable
> contained an anchor like \A it does not make a lot of sense to call
> gsub... because it would at most match once!
>
> So while we may not know the exact content of the regexp (eg, it is
> provided by another component, etc), it makes sense to think that at
> least we know it is suited to a global replace (if not, what exactly are
> we doing? the whole thing loses any sense).

Of course I understand JGII's point, but he does not understand mine.
The regexp gsub example is totally irrelevant in its particularity ---
the whole point was the use of Object#as with a block which *cannot*
be replaced with a standard method.  It could be any block at all ---
it doesn't matter what it is.

Instead of trying to understand the overall aim, which was the use of
Object#as, JGII decided to be pedantic about the contents of the
block.  Well, two can play the pedantry game.  He wanted to replace
the block with concat(), which is simply incorrect.  But, again,
nonetheless irrelevant.

Due to the distracting cruft in this thread, I'll start another one.
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-11-13 08:39
(Received via mailing list)
On Nov 12, 2007 10:55 PM,  <furtive.clown@gmail.com> wrote:
> > >       end
> context.  Why would I even risk doing that?  What if I want to use the
> special name.  It should be given a temporary name (e.g. "t") which
> communicates its temporal non-specialness.  Object#instance_eval is
> the former, Object#as is the latter.
>
>
>

Understood, I actually just started to grasp the concept of "this", thx.
R.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-13 14:36
(Received via mailing list)
On Nov 13, 2007, at 12:20 AM, furtive.clown@gmail.com wrote:

>> exactly are
>> we doing? the whole thing loses any sense).
>
> Of course I understand JGII's point, but he does not understand mine.

Careful there.  Let's not turn this into name calling.

> Instead of trying to understand the overall aim, which was the use of
> Object#as, JGII decided to be pedantic about the contents of the
> block.

I remember this going down as:

1.  You showed as() and said you could provide examples of why it's
so important
2.  I said I didn't get it an would like to see those examples
3.  You showed examples
4.  I said that I felt those examples didn't properly show as() to be
a must have, for various reasons
5.  It became clear we just disagreed about that

> He wanted to replace the block with concat(), which is simply
> incorrect.

Can you show how my concat() solution was "incorrect?"  You had code
like:

 >> (1..3).map { |n| n * 2 } + [4, 5]
=> [2, 4, 6, 4, 5]

I suggested using:

 >> (1..3).map { |n| n * 2 }.concat([4, 5])
=> [2, 4, 6, 4, 5]

They look equivalent to me.

Under the hood concat() is a destructive change to the original Array
whereas the + operator creates a new Array.  However, since we used
map() first to generate an Array, that doesn't affect us here.

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 15:15
(Received via mailing list)
On Nov 13, 8:35 am, James Edward Gray II <ja...@grayproductions.net>
wrote:
> Can you show how my concat() solution was "incorrect?"  You had code
> like:

I'm sorry, I meant your removal of the gsub block was incorrect.

   class Object
      def as
         yield self
      end
   end

   input = %w(a b)
   re = %r!a\Z!

   filename = input.map { |t|
      t.gsub(re, "_")
   }.join.as { |t|
      File.basename(t)
   }

   puts filename
   # => _b

   file_name = File.basename(input.join.gsub(re, "_"))

   puts file_name
   # => ab

It is not name-calling to observe that you don't understand my
argument.  My apologies; no offense was intended.  Nonetheless I still
believe that you haven't followed what I've said in this thread.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-13 15:19
(Received via mailing list)
On Nov 13, 2007, at 8:15 AM, furtive.clown@gmail.com wrote:

>       t.gsub(re, "_")
>    }.join.as { |t|
>       File.basename(t)
>    }
>
>    puts filename
>    # => _b

Just for the sake of the archives, note that the above should use sub
() instead of gsub().

Thanks for the explanation.

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 15:35
(Received via mailing list)
On Nov 13, 9:18 am, James Edward Gray II <ja...@grayproductions.net>
wrote:
>
> Just for the sake of the archives, note that the above should use sub
> () instead of gsub().
>

But that is part of what you are not understanding.  'input' and 're'
were originally presented as unknowns.  You can't change gsub to sub,
just like you can't take out the block.
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-13 15:39
(Received via mailing list)
On Nov 13, 2007, at 8:35 AM, furtive.clown@gmail.com wrote:

> On Nov 13, 9:18 am, James Edward Gray II <ja...@grayproductions.net>
> wrote:
>>
>> Just for the sake of the archives, note that the above should use sub
>> () instead of gsub().
>
> But that is part of what you are not understanding.  'input' and 're'
> were originally presented as unknowns.  You can't change gsub to sub,
> just like you can't take out the block.

I'm uncomfortable with that line of thinking.  When I don't know what
something contains, I don't default to the most damaging choice.

James Edward Gray II
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 15:50
(Received via mailing list)
On Nov 13, 9:38 am, James Edward Gray II <ja...@grayproductions.net>
wrote:
> > just like you can't take out the block.
>
> I'm uncomfortable with that line of thinking.  When I don't know what
> something contains, I don't default to the most damaging choice.
>

The code was given as is, with unspecified variables 'input' and
're'.  You suggested removing the block.  That is wrong because the
behavior is not equivalent, as I just demonstrated.  You suggested
changing gsub to sub.  That is also wrong because the behavior is not
equivalent (and I assume this needs no demonstration).
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-13 16:06
(Received via mailing list)
On Nov 13, 2007, at 8:50 AM, furtive.clown@gmail.com wrote:

> You suggested changing gsub to sub.  That is also wrong because the
> behavior is not equivalent (and I assume this needs no demonstration).

Your code:

   class Object
      def as
         yield self
      end
   end

   input = %w(a b)
   re = %r!a\Z!

   filename = input.map { |t|
      t.gsub(re, "_")
   }.join.as { |t|
      File.basename(t)
   }

   puts filename
   # => _b

Switching to sub():

   class Object
      def as
         yield self
      end
   end

   input = %w(a b)
   re = %r!a\Z!

   filename = input.map { |t|
      t.sub(re, "_")
   }.join.as { |t|
      File.basename(t)
   }

   puts filename
   # => _b

The results were the same.

James Edward Gray II
851acbab08553d1f7aa3eecad17f6aa9?d=identicon&s=25 Ken Bloom (Guest)
on 2007-11-13 16:11
(Received via mailing list)
On Mon, 12 Nov 2007 19:31:08 -0500, Raul Parolari wrote:

>>
> contained an anchor like \A it does not make a lot of sense to call
> gsub... because it would at most match once!
>
> So while we may not know the exact content of the regexp (eg, it is
> provided by another component, etc), it makes sense to think that at
> least we know it is suited to a global replace (if not, what exactly are
> we doing? the whole thing loses any sense).

OK. But if re isn't suited to a global replace, then we're using sub
instead of gsub. If we take the following examples,

filename = File.basename(input.map { |t|
   t.sub(re, "_")
}.join)

filename = input.map { |t|
   t.sub(re, "_")
}.join.as { |t|
   File.basename(t)
}

then neither of the following are equivalent

file_name = File.basename(input.join.sub(re, "_"))
file_name = File.basename(input.join.gsub(re, "_"))

--Ken
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 16:30
(Received via mailing list)
On Nov 13, 10:04 am, James Edward Gray II <ja...@grayproductions.net>
wrote:
> The results were the same.

Not for all cases of 're' and 'input'.  Those variables were
unspecified.  If you wish to suggest an improvement to the code, it
must at least be equivalent to what was given.  It must produce the
same results for all such 're' and all such 'input'.  Sorry, the best
I can do is say it again:

The code was given as is, with unspecified variables 'input' and
're'.  You suggested removing the block.  That is wrong because the
behavior is not equivalent, as I just demonstrated.  You suggested
changing gsub to sub.  That is also wrong because the behavior is not
equivalent (and I assume this needs no demonstration).

But I guess it does need a demonstration:

   class Object
      def as
         yield self
      end
   end

   input = %w(aa bb)
   re = %r!a!

   filename = input.map { |t|
      t.gsub(re, "_")
   }.join.as { |t|
      File.basename(t)
   }

   puts filename
   # => __bb

   filename = input.map { |t|
      t.sub(re, "_")
   }.join.as { |t|
      File.basename(t)
   }

   puts filename
   # => _abb

Not equivalent.
703fbc991fd63e0e1db54dca9ea31b53?d=identicon&s=25 Robert Dober (Guest)
on 2007-11-13 19:03
(Received via mailing list)
On Nov 13, 2007 4:30 PM,  <furtive.clown@gmail.com> wrote:
I believe Object#as is a nice idea, I might even use it with your
permission;)
However there is no need to make all that fuss about people who do not
agree, it is their choice and I feel that you kind of kill a nice
threat.
Just my 0.02€

Robert
1bac2e65d64faf472cf2ebc94f0f5ee0?d=identicon&s=25 ara.t.howard (Guest)
on 2007-11-13 19:11
(Received via mailing list)
On Nov 12, 2007, at 11:20 PM, furtive.clown@gmail.com wrote:

> Object#as, JGII decided to be pedantic about the contents of the

highly unlikely: james is about as nice a guy as you'll meet online.
email isn't the greatest medium to attempt to determine tone and i
doubt he intended to come across like that - people rarely do.

thought i'd mention that as it's worth considering, on this list more
that others.

kind regards.

a @ http://codeforpeople.com/
A13fa5a4daf7df938f26f2b57795b6a0?d=identicon&s=25 unknown (Guest)
on 2007-11-13 20:40
(Received via mailing list)
On Nov 13, 1:03 pm, Robert Dober <robert.do...@gmail.com> wrote:
> I believe Object#as is a nice idea, I might even use it with your permission;)
> However there is no need to make all that fuss about people who do not
> agree, it is their choice and I feel that you kind of kill a nice
> threat.
> Just my 0.02€

There is nothing wrong with disagreement --- but one has to understand
the issue on the table before taking a position.  The difficult part
of the thread was that James insisted he disagreed with me when he
clearly didn't understand what I was saying, as especially the last
few posts show.

Also, an example is meant to illustrate a point, and it is difficult
when a person focuses on irrelevant parts of the example instead of
asking what the example is meant to illustrate.  My remark about
pedantry was not meant as derogatory but descriptive: "adj. overly
concerned with minute details or formalisms."  That is exactly what
was happening --- the forest for the trees.

Perhaps one of my character flaws is my high tolerance for technical
exchanges in really trying to nail down the issues and to clarify the
arguments: at times I keep going while others see it as excessive or
diminutive.  It is very hard for me to allow misunderstandings to be
applied toward a position of agreement or disagreement.

I apologize again --- I was not feeling spiteful or malicious in this
exchange, contrary to what it may have appeared like.  It is just my
obsessive nature.  Cheers to all, and to all a good night.

--FC
E0526a6bf302e77598ef142d91bdd31c?d=identicon&s=25 Daniel DeLorme (Guest)
on 2007-11-14 02:08
(Received via mailing list)
Robert Dober wrote:
> On Nov 13, 2007 4:30 PM,  <furtive.clown@gmail.com> wrote:
> I believe Object#as is a nice idea, I might even use it with your permission;)
> However there is no need to make all that fuss about people who do not
> agree, it is their choice and I feel that you kind of kill a nice
> threat.

"These are not bad people, they're just programmers, and programming
requires you to dot every i and cross every t, so you get into a frame
of mind where you can't leave any argument unanswered any more than you
would ignore an error from your compiler." -- Joel Spolsky

:-)
289cf19aa581c445915c072bf45c5e25?d=identicon&s=25 Todd Benson (Guest)
on 2007-11-14 03:02
(Received via mailing list)
On Nov 12, 2007 1:10 PM,  <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.  All of my ruby scripts use
>
>    class Object
>       def as
>          yield self
>       end
>    end

This can be useful in the right context.  Should it be canonized in
Ruby?  I'm not sure.

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

This method chaining looks ugly to me, but I'm not a CS guy; so, to
each his own.

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

You are suggesting that people add something that probably doesn't
need to be added.  I'm halfway convinced, but I think you should
present a couple more use cases for me to sway your way (sorry, folks,
for the poetic language :-)

Todd
6087a044557d6b59ab52e7dd20f94da8?d=identicon&s=25 Peña, Botp (Guest)
on 2007-11-15 14:27
(Received via mailing list)
From: furtive
# On Nov 12, 2:31 pm, Martin
# > #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.

(my previous message seems to have lost, so i apologize for a 2nd post)

tap is indispensable for me when it comes to chaining (specially bang
methods)..

naive example follows...

~> s
=> "This is a test"

~> s.capitalize!.capitalize!
NoMethodError: undefined method `capitalize!' for nil:NilClass

breaks!

~> s.upcase!.upcase!
=> nil

breaks!

~> s.upcase!.capitalize!
NoMethodError: undefined method `capitalize!' for nil:NilClass

breaks!

~> s.tap(&:upcase!).tap(&:upcase!).tap(&:capitalize!).tap(&:capita
lize!).tap(&:downcase!).tap(&:downcase!)
=> "this is a test"

long chain, no break :)

btw, i like the #tap name, it's like tapping a chain so that it wont
break. (i'm not an english expert but that is how i usually use the
word)..

kind regards -botp
6087a044557d6b59ab52e7dd20f94da8?d=identicon&s=25 Peña, Botp (Guest)
on 2007-11-15 14:34
(Received via mailing list)
From: furtive.clown@gmail.com [mailto:furtive.clown@gmail.com]
# 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.

~> s
=> "THIS IS A TEST"
~> s.upcase!.capitalize!
NoMethodError: undefined method `capitalize!' for nil:NilClass
...
~> s.tap{|x| x.upcase!}.capitalize!
=> "This is a test"
~> s
=> "This is a test"

C:\ruby1.9\bin>ri Object#tap -T
------------------------------------------------------------- Object#tap
     obj.tap{|x|...}    => obj
------------------------------------------------------------------------
     Returns the receiver after executing the block given. Its main
     purpose is to be inserted in the method chain.

kind regards -botp
The essence of knowledge, is having it, to apply it;..
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2007-11-15 14:42
(Received via mailing list)
Hi --

On Thu, 15 Nov 2007, Peña, Botp wrote:

> # usefulness, and I can show reams of examples in addition to the above
>
> ~> s.upcase!.capitalize!
> btw, i like the #tap name, it's like tapping a chain so that it wont break. (i'm not an 
english expert but that is how i usually use the word)..
I am an English expert and I have no idea what "tap" is supposed to
have to do with what this #tap method does.


David
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2007-11-15 14:47
(Received via mailing list)
On Nov 15, 2007, at 7:41 AM, David A. Black wrote:

> I am an English expert and I have no idea what "tap" is supposed to
> have to do with what this #tap method does.

It's like "tapping" a call chain, just as you would tap a phone call.

James Edward Gray II
F53b05cdbdf561cfe141f69b421244f3?d=identicon&s=25 David A. Black (Guest)
on 2007-11-15 14:55
(Received via mailing list)
Hi --

On Thu, 15 Nov 2007, James Edward Gray II wrote:

> On Nov 15, 2007, at 7:41 AM, David A. Black wrote:
>
>> I am an English expert and I have no idea what "tap" is supposed to
>> have to do with what this #tap method does.
>
> It's like "tapping" a call chain, just as you would tap a phone call.

It's like tapping a phone call after the call is over :-)


David
6087a044557d6b59ab52e7dd20f94da8?d=identicon&s=25 Peña, Botp (Guest)
on 2007-11-16 02:14
(Received via mailing list)
From: David A. Black [mailto:dblack@rubypal.com]
# On Thu, 15 Nov 2007, James Edward Gray II wrote:
# > On Nov 15, 2007, at 7:41 AM, David A. Black wrote:
# >> I am an English expert and I have no idea what "tap" is supposed to
# >> have to do with what this #tap method does.
# > It's like "tapping" a call chain, just as you would tap a phone
call.
# It's like tapping a phone call after the call is over :-)
# David

pardon me sir davide, but i am not sure what you mean there, in my
examples, i think tap tapped before it called upcase/capitalize, no?

kind regards -botp
163755a5d3a5c57bd79c4f41bdda7a22?d=identicon&s=25 Clifford Heath (Guest)
on 2007-11-16 03:10
(Received via mailing list)
furtive.clown@gmail.com wrote:
> }
>
> I greatly prefer the latter.  I want to chain, chain, chain, and I
> don't want pesky prefix-y function calls like File.basename() to cramp
> my style.

It wasn't entirely a coincidence that JEGII found concat - it's
the sort of thing that is often needed with arrays. The underlying
problem in your example is that filename operations aren't object-
oriented, so you wind up using a method-call on a class... yuk.

This is a weakness in the standard classes - there should be a
Filename class, and a String#to_filename method, so your example
becomes:

filename = input.map { |t|
   t.gsub(re, "_")
}.join.to_filename.base

No need for Object#as in that. There are other use cases of course,
and I also might use "as" sometimes myself.

Clifford Heath.
4828d528e2e46f7c8160c336eb332836?d=identicon&s=25 Robert Heiler (shevegen)
on 2007-11-16 04:41
Not sure that I am able to contribue to this discussion, but I must
admit neither .tap nor .affect are very intuitive for me on first glance
:)
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.