#returning and #tap

Thought of that, but not everything responds to dup (nil and symbols, e.g.)

m.

···

On 11/17/06, spooq <spoooq@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

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.

Sweet!

···

On Fri, 17 Nov 2006 11:57:58 -0000, <dblack@wobblini.net> wrote:

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

--
Ross Bamford - rosco@roscopeco.remove.co.uk

Can those cases be worked around?

···

On 11/17/06, Martin DeMello <martindemello@gmail.com> wrote:

On 11/17/06, spooq <spoooq@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.)

Nice. Sounds very reusable.

···

On 11/17/06, Trans <transfire@gmail.com> wrote:

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?

Noooooooooooo! Stop this meme now before it hurts someone!

···

On 11/17/06, Ross Bamford <rosco@roscopeco.remove.co.uk> wrote:

On Fri, 17 Nov 2006 11:57:58 -0000, <dblack@wobblini.net> wrote:

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

Sweet!

Hi --

···

On Fri, 17 Nov 2006, Ross Bamford wrote:

On Fri, 17 Nov 2006 11:57:58 -0000, <dblack@wobblini.net> wrote:

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

Sweet!

I rather like it too :slight_smile: I guess if too many of them got chained it
could get cumbersome. But it does allow one to avoid stopping and
starting the chaining based on the nil returns.

David

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Hi --

···

On Fri, 17 Nov 2006, spooq wrote:

On 11/17/06, Martin DeMello <martindemello@gmail.com> wrote:

On 11/17/06, spooq <spoooq@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.)

Can those cases be worked around?

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

David

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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?

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

David

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

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.

···

On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

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

Hi --

···

On Fri, 17 Nov 2006, spooq wrote:

On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

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

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.

I'm afraid I don't follow. Can you show how you'd write this in the
ways you've described? Since you can't reliably chain gsub! with
anything else, I'm not sure how it would play out.

David

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

What I mean is to chain with gsub instead, or to make it obvious that
you are changing the original by using separate lines.

In effect, you want to do this...

foo.gsub!.bar

I'm saying, if you want functional style chaining, obey the unwritten
rules and don't have side-effects in the chain.

foo.gsub.bar

If you want to modify the original, use traditional imperative style
with side-effects.

foo.gsub!
foo.bar

Mixing the two styles is just going to cause confusion in long chains.
Think of it this way... do you care about foo, or about the return
value of foo.gsub ? By modifying in a chain, you're saying you care
about both, even though they have the same value. Not a problem, you
say? Then think about this case....

foo.gsub!.gsub.gsub!

I feel sorry for the maintenance programmer already.

···

On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

I'm afraid I don't follow. Can you show how you'd write this in the
ways you've described? Since you can't reliably chain gsub! with
anything else, I'm not sure how it would play out.

I think I need to make this more explicit for it to be clear. Only the
gsub!'s need to be wrapped in tap()'s... the gsub must be directly
inline because of the need to use it's return value. Wrapping it is
equivalent to deleting it from the chain. My point is really about it
not being clear when foo itself is being passed along, as opposed to
something derived from applying a function to foo. Does that make more
sense? I get the feeling I'm not explaining myself very well.

foo.tap { gsub! }.gsub.tap { gsub! }

should be written as

foo.gsub!
foo.gsub.gsub

···

On 11/20/06, spooq <spoooq@gmail.com> wrote:

On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

> I'm afraid I don't follow. Can you show how you'd write this in the
> ways you've described? Since you can't reliably chain gsub! with
> anything else, I'm not sure how it would play out.

What I mean is to chain with gsub instead, or to make it obvious that
you are changing the original by using separate lines.

In effect, you want to do this...

foo.gsub!.bar

I'm saying, if you want functional style chaining, obey the unwritten
rules and don't have side-effects in the chain.

foo.gsub.bar

If you want to modify the original, use traditional imperative style
with side-effects.

foo.gsub!
foo.bar

Mixing the two styles is just going to cause confusion in long chains.
Think of it this way... do you care about foo, or about the return
value of foo.gsub ? By modifying in a chain, you're saying you care
about both, even though they have the same value. Not a problem, you
say? Then think about this case....

foo.gsub!.gsub.gsub!

I feel sorry for the maintenance programmer already.

Hi --

> I'm afraid I don't follow. Can you show how you'd write this in the
> ways you've described? Since you can't reliably chain gsub! with
> anything else, I'm not sure how it would play out.

What I mean is to chain with gsub instead, or to make it obvious that
you are changing the original by using separate lines.

In effect, you want to do this...

foo.gsub!.bar

I'm saying, if you want functional style chaining, obey the unwritten
rules and don't have side-effects in the chain.

foo.gsub.bar

You're mixing real and quasi-pseudo-code examples here, though. One
would never actually do:

   foo.gsub!.bar

because of the danger of nil from gsub!. That was the impetus for my
idea of using tap in this situation.

If you want to modify the original, use traditional imperative style
with side-effects.

foo.gsub!
foo.bar

Mixing the two styles is just going to cause confusion in long chains.
Think of it this way... do you care about foo, or about the return
value of foo.gsub ? By modifying in a chain, you're saying you care
about both, even though they have the same value. Not a problem, you
say? Then think about this case....

foo.gsub!.gsub.gsub!

I feel sorry for the maintenance programmer already.

The ! is there to give a heads-up for possible side-effects. Also, I
think I have a different view of chaining than you do. I view
chaining very strictly left-to-right. So say you've got this:

   foo.any_method_whatsoever

Once that call is done, it's done. If this is added to it:

   .some_other_method

that's a completely new transaction.

In other words, array.sort! in this snippet:

   array.sort!.map {|s| s.capitalize }

is no more or less imperative than array.sort! in this snippet:

   array.sort!
   array.map {|s| s.capitalize }

The snag is that a lot of these in-place change methods in Ruby return
nil when no change happens. Otherwise it would be perfectly fine to
do:

   string.gsub!(...).each_byte {...}

gsub! is documented as performing in-place changes, so there's no
stealth or ambiguity at all -- *except* the nil issue, which is indeed
a deal-breaker. Hence the tap solution.

I think I need to make this more explicit for it to be clear. Only the
gsub!'s need to be wrapped in tap()'s... the gsub must be directly
inline because of the need to use it's return value. Wrapping it is
equivalent to deleting it from the chain. My point is really about it
not being clear when foo itself is being passed along, as opposed to
something derived from applying a function to foo. Does that make more
sense? I get the feeling I'm not explaining myself very well.

foo.tap { gsub! }.gsub.tap { gsub! }

should be written as

foo.gsub!
foo.gsub.gsub

You'd want a final ! there to make them equivalent :slight_smile: And you'd only
need one tap. In any case, as long as one knows what tap does, and
what gsub! does, there's no ambiguity or unclarity with the tap
version; it's all out in the open.

I'll just add that in my own little testbeds, I've renamed tap to
"punt" :slight_smile: The idea being: punt the object across this method call
and block -- but still, of course, execute the block in full, with the
object passed in.

We're probably at the agree-to-disagree stage, but hopefully it's been
of interest to both of us and anyone interested in the topic.

David

···

On Mon, 20 Nov 2006, spooq wrote:

On 11/20/06, spooq <spoooq@gmail.com> wrote:

On 11/17/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org

Hi --

   foo.gsub!.bar

because of the danger of nil from gsub!. That was the impetus for my
idea of using tap in this situation.

Sorry, this was a really bad example from me... I meant the equivalent
using tap notation - foo.tap { gsub! }.bar. There was no way for you
to guess that.

The ! is there to give a heads-up for possible side-effects. Also, I
think I have a different view of chaining than you do.

Maybe you are more confident of your ability to handle complexity than
I am of my ability :slight_smile:

> foo.tap { gsub! }.gsub.tap { gsub! }
>
> should be written as
>
> foo.gsub!
> foo.gsub.gsub

You'd want a final ! there to make them equivalent :slight_smile:

Are you sure? Let's look at the original again.

foo.tap { gsub! }.gsub.tap { gsub! }

foo.tap { gsub! } -> returns a modified foo
foo.gsub -> returns the result of
foo.gsub, which IS NOT FOO
returned_value.tap { gsub! } -> modifies the not-foo and returns it

Somewhere along that path, you have gone from passing foo to passing
an anonymous not-foo, and it's not poke-you-in-the-eye obvious where
that was. If you now expect foo to be returned by this chain (as you
might think when you see the first tap), you will be surprised. If you
expect foo to be modified by the last gsub, you will be surprised. If
you expect chains to leave the original alone (like all the inherently
chainable functions in Ruby do at the moment), you will be surprised.
Thats far too many surprises for my liking.

So how can we rewrite... the anonymous not-foo is only useful as a
return value, seeing as it has no name and all. Let's make that
explicit.

foo.gsub!
puts foo, somefunc(foo.gsub.gsub)

Almost by definition, if you're using a bang-function, you need foo
later on, otherwise you would just do

puts somefunc(foo.gsub.gsub.gsub)

and be done with it.

Conclusion : tap-allowing-side-effects and the equivalent,
bang-functions returning self, add nothing except headaches!

I'll just add that in my own little testbeds, I've renamed tap to
"punt" :slight_smile:

punt seems like an equally valid name.

We're probably at the agree-to-disagree stage, but hopefully it's been
of interest to both of us and anyone interested in the topic.

We probably are at that stage, but the journey was definitely
worthwhile, so thanks :slight_smile:

···

On 11/20/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

On Mon, 20 Nov 2006, spooq wrote:
> On 11/20/06, spooq <spoooq@gmail.com> wrote:

Hi --

We're probably at the agree-to-disagree stage, but hopefully it's been
of interest to both of us and anyone interested in the topic.

We probably are at that stage, but the journey was definitely
worthwhile, so thanks :slight_smile:

I'll just add that I now see exactly what you mean about mixing the
two techniques. And definitely -- one would want to be very careful
and selective. One variation on the theme might be cases where
in-place operations saved some memory, even if you end up throwing the
object away; so the dual fact that the end result was a different
object, *and* one of the intermediate objects had in-place stuff done
to it, might be OK.

David

···

On Mon, 20 Nov 2006, spooq wrote:

--
                   David A. Black | dblack@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] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org