RCR enumerable extra into core

I'm considering suggesting that the base functionality for the
enumerable-extra gem[1] be submitted into core.

In a nut shell, it's that you can do

list.map :name

as an alternative to

list.map &:name

which is arguably less readable.
Thoughts?
-r
[1] http://allgems.ruby-forum.com/docs/enumerable-extra/0.1.2/doc/hanna/

···

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

list.map :name

Makes sense, given that you can already do

list.reduce(:+)

Tor Erik

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

I'm considering suggesting that the base functionality for the
enumerable-extra gem[1] be submitted into core.

In a nut shell, it's that you can do

list.map :name

as an alternative to

list.map &:name

which is arguably less readable.
Thoughts?

I'm very much in sympathy with the position that &:name is ugly. It's
always struck me as uncharacteristically line-noise-ish for Ruby
(though I also understand the motivation for it). I have trouble
talking myself into using it.

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Having :name magically understood as &:name is, for my taste, too much
invisible ink. It would also mean that map (and maybe other Enumerable
methods) had one argument syntax and all other iterators had another,
since presumably there's no general way to make method(:sym) know when
it's supposed to actually mean method(&:sym). I don't think that's a
useful special case.

(I know there's inject(:method), but note that that's a different
case; inject(:method) is not shorthand for inject(&:method).)

David

···

On Sun, 8 Nov 2009, Roger Pack wrote:

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

I garbled that bit, due to an irb session gone horribly wrong :slight_smile: So
forget that sentence. I do have a feeling there's something my brain
is groping toward that differentiates the inject case, but I could be
totally wrong about that.

David

···

On Sun, 8 Nov 2009, David A. Black wrote:

(I know there's inject(:method), but note that that's a different
case; inject(:method) is not shorthand for inject(&:method).)

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

I think list.map :name
is clearly to be understood as list.map { |x| x.send(:name) }
not as an abbreviation for list.map &:name
which I agree is extraordinarily ugly.

I think (don't quote me) allowing #map to take a symbol was debated
and rejected a long time ago. I'd like to see it implemented in core,
as it's a shortcut for a common idiom, reads nicely and is
unambiguous. I don't know when list.inject(:+) started working, but
it's a good thing!

One thing occurred to me, though. Using "send" in the implementation
would allow access to private methods. A check would need to be made
before pulling the trigger.

···

On Nov 8, 11:57 am, "David A. Black" <dbl...@rubypal.com> wrote:

I'm very much in sympathy with the position that &:name is ugly. It's
always struck me as uncharacteristically line-noise-ish for Ruby
(though I also understand the motivation for it). I have trouble
talking myself into using it.

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Having :name magically understood as &:name is, for my taste, too much
invisible ink. [...]

--
Gavin Sinclair

which is arguably less readable.
Thoughts?

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Hmm. I suppose I'm of a slightly different opinion--to me
list.map &:symbol

is less clear than
list.map(:symbol)

If we were required to write
list.map &:symbol.to_proc

then I would be more easily convinced that list.map(:symbol) is too
implicit. So for me it's implicit already.

I'm not too worried about the run time slowdown...so many methods are
already special cased...it doesn't seem to be a too large concern to the
core guys...

-r

···

--
Posted via http://www.ruby-forum.com/\.

David A. Black wrote:

It would also mean that map (and maybe other Enumerable
methods) had one argument syntax and all other iterators had another,
since presumably there's no general way to make method(:sym) know when
it's supposed to actually mean method(&:sym).

As you say, it could be extended to other Enumerable methods which
currently don't take an argument. Maybe useful for:

    students.max(:age)

whereas currently you would have to write something like

    students.map(&:age).max

or

    students.max { |a,b| a.age <=> b.age }.age

[Note that max(&:age) doesn't work]

Thinking aloud: methods which iterate over a collection would be
expected to replace each elem with elem.send(*args) if given arguments.
And this could be simplified by delegating that job to the 'each'
method.

  class Array
    alias :old_each :each
    def each(*args, &blk)
      if args.empty?
        old_each(&blk)
      else
        old_each { |elem| yield elem.send(*args) }
      end
    end
  end

  module Enumerable
    def max(*args, &cmp)
      cmp ||= Proc.new { |a,b| a<=>b }
      first = true
      res = nil
      each(*args) do |elem| # << NOTE *args passed down
        if first
          res = elem
          first = false
          next
        end
        res = elem if cmp[elem, res] > 0
      end
      res
    end
  end

  class Person
    attr_accessor :name, :age
    def initialize(name, age)
      @name, @age = name, age
    end
  end

  students =
  students << Person.new("Alice",35)
  students << Person.new("Bob",33)
  puts students.max(:age)

But I don't feel strongly that this is worthwhile, as IMO the language
is more than complicated enough already.

In any case, it was already decided that the "right" way to pass
arguments to an Enumerable was to create a new Enumerator proxy object
for it. That is, even if 'each' did take arguments, you're supposed to
write

  students.to_enum(:each, :age).max

rather than max taking arguments and passing them through.

···

--
Posted via http://www.ruby-forum.com/\.

list.map :name
+1

···

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

Hi --

I'm very much in sympathy with the position that &:name is ugly. It's
always struck me as uncharacteristically line-noise-ish for Ruby
(though I also understand the motivation for it). I have trouble
talking myself into using it.

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Having :name magically understood as &:name is, for my taste, too much
invisible ink. [...]

I think list.map :name
is clearly to be understood as list.map { |x| x.send(:name) }
not as an abbreviation for list.map &:name
which I agree is extraordinarily ugly.

I think (don't quote me) allowing #map to take a symbol was debated
and rejected a long time ago.

It was: http://oldrcrs.rubypal.com/rejected.html#rcr50\. Needless to
say that doesn't contractually bind Matz to any eternal decision :slight_smile:

There's no doubt it looks better than &:sym. My problem with it is
that it adds a special case (or a small subclass of special cases). I
don't think it would be a disaster, but I always dislike things where
it feels like it has to be accounted for sort of inside out (there's
the general case of &object, the common case of &:sym, and now a kind
of escape from &:sym via :sym).

Mind you, I really don't think it would be a calamity, just a little
hard to account for in terms of what's around it.

I'd like to see it implemented in core, as it's a shortcut for a
common idiom, reads nicely and is unambiguous. I don't know when
list.inject(:+) started working, but it's a good thing!

One thing occurred to me, though. Using "send" in the implementation
would allow access to private methods. A check would need to be made
before pulling the trigger.

You could use public_send.

David

···

On Sun, 8 Nov 2009, Gavin Sinclair wrote:

On Nov 8, 11:57 am, "David A. Black" <dbl...@rubypal.com> wrote:

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Roger Pack wrote:

which is arguably less readable.
Thoughts?

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Hmm. I suppose I'm of a slightly different opinion--to me
list.map &:symbol

is less clear than
list.map(:symbol)

If we were required to write
list.map &:symbol.to_proc

But we are. &:symbol is equivalent to :symbol.to_proc. So the & form
is explicit -- and thus arguably clearer. I would almost expect
list.map :symbol to return :symbol for each item in list.

then I would be more easily convinced that list.map(:symbol) is too
implicit. So for me it's implicit already.

Because you're misunderstanding the syntax.

I'm not too worried about the run time slowdown...so many methods are
already special cased...it doesn't seem to be a too large concern to the
core guys...

-r

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Hi --

which is arguably less readable.
Thoughts?

At the same time, the merit of &:name over :name is that &:name does
tell you what's going on, once you know that the & in front of any
object (symbol or otherwise) in last-argument position means that the
object will be converted to a Proc and play the block role.

Hmm. I suppose I'm of a slightly different opinion--to me
list.map &:symbol

is less clear than
list.map(:symbol)

I don't think that &:symbol is inherently clear, but once you know the
rule, then it's just an example of the rule.

If we were required to write
list.map &:symbol.to_proc

then I would be more easily convinced that list.map(:symbol) is too
implicit. So for me it's implicit already.

Part of the problem I think is that map(:symbol) wouldn't really be an
abbreviation of map(&:symbol) (which would presumably still work); it
would be a new semantics for map, namely that map would now take an
argument, but it would look a lot like shorthand for &:symbol. In a
way I wish they were more different, so that they wouldn't have to be
explained in terms of each other (which I guarantee is how they'll be
seen).

I'm not too worried about the run time slowdown...so many methods are
already special cased...it doesn't seem to be a too large concern to the
core guys...

Or, looking at it the other way, maybe there are so many special cases
that we've reached the quota :slight_smile: That's always the flip-side of the
precedent reasoning.

David

···

On Tue, 10 Nov 2009, Roger Pack wrote:

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Not completely on-point, but I tend to be rather conservative when
thinking about such proposed changes.

I've recently taken over an existing Rails app, one of the first tasks
was to get it to run with Ruby 1.9.

Most of the changes I needed to make turned out to do with the fact
that 1.9 redefined Array#to_s

Where prior to 1.9 Array#to_s was the same as Array#join so:

  [1, nil, "foo"].to_s => "1foo"

in 1.9 it's been redefined to be the same as Array#inspect so:

[1, nil, "foo"].to_s => "[1, nil, \"foo\"]"

I was surprised at how many times an array was used in a context which
expected the pre-1.9 behavior.

When I explained to the client that so many changes were caused
because Ruby 1.9 changed Array#to_s his response was "Why did they do
that?"

Of course the upside of such changes is that they generate billable
hours, but I'd rather generate more value per hour. But c'est la vie!

···

On Sun, Nov 8, 2009 at 9:39 AM, David A. Black <dblack@rubypal.com> wrote:

Mind you, I really don't think it would be a calamity, just a little
hard to account for in terms of what's around it.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

list.map(:symbol) is just like object.send(:symbol),except the :symbol
for list.
so list.map(:symbol) is good~

···

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

list.map(:name)

The next question...

we currently have
list.map => Enumerator

so should
list.map(:method)

best return an array or an enumerator? (though I don't really understand
why enumerators are cool, apparently they've become quite common lately)
Thoughts?

list.map(:method, arg1)

This would be a natural extension of being able to do list.map(:method)

but it feels less intuitive than forcing the user to do

list.map{|i| i.method(arg) }

Or does it?
Thoughts?
-r

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

···

On Mon, 9 Nov 2009, Rick DeNatale wrote:

On Sun, Nov 8, 2009 at 9:39 AM, David A. Black <dblack@rubypal.com> wrote:

Mind you, I really don't think it would be a calamity, just a little
hard to account for in terms of what's around it.

Not completely on-point, but I tend to be rather conservative when
thinking about such proposed changes.

I've recently taken over an existing Rails app, one of the first tasks
was to get it to run with Ruby 1.9.

Most of the changes I needed to make turned out to do with the fact
that 1.9 redefined Array#to_s

Where prior to 1.9 Array#to_s was the same as Array#join so:

[1, nil, "foo"].to_s => "1foo"

in 1.9 it's been redefined to be the same as Array#inspect so:

[1, nil, "foo"].to_s => "[1, nil, \"foo\"]"

I was surprised at how many times an array was used in a context which
expected the pre-1.9 behavior.

When I explained to the client that so many changes were caused
because Ruby 1.9 changed Array#to_s his response was "Why did they do
that?"

Of course the upside of such changes is that they generate billable
hours, but I'd rather generate more value per hour. But c'est la vie!

I'll take on all comers in the matter of being conservative about
changes to Ruby :slight_smile: Part of the case with the examples you're
referring to, though, is the fact that 1.9 is more of a major-number
change, in spirit, than it sounds like.

David

--
The Ruby training with D. Black, G. Brown, J.McAnally
Compleat Jan 22-23, 2010, Tampa, FL
Rubyist http://www.thecompleatrubyist.com

David A. Black/Ruby Power and Light, LLC (http://www.rubypal.com)

Haoqi Haoqi wrote:

list.map(:symbol) is just like object.send(:symbol),except the :symbol
for list.

But it isn't. The semantics of the two are completely different. You
can only send a method name, but a naked method name doesn't make any
sense as an argument to map.

so list.map(:symbol) is good~

Because you don't understand what's going on? No thanks.
Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Roger Pack wrote:
[...]

list.map(:method, arg1)

This would be a natural extension of being able to do list.map(:method)

but it feels less intuitive than forcing the user to do

list.map{|i| i.method(arg) }

Or does it?
Thoughts?

Questions like this point to the very flaws that indicate that list.map
:method is a bad idea. If you understand :symbol.to_proc, or its
abbreviation &:symbol, there is no need for the proposed syntax.

-r

Best,

···

--
Marnen Laibow-Koser
http://www.marnen.org
marnen@marnen.org
--
Posted via http://www.ruby-forum.com/\.

Roger Pack wrote:

we currently have
list.map => Enumerator

so should
list.map(:method)

best return an array or an enumerator?

The ultimate conclusion of this: all Enumerable methods should return
other Enumerators. When you want a real array, then add .to_a to the
end.

(though I don't really understand
why enumerators are cool, apparently they've become quite common lately)

It's cool because evaluation takes place "left to right" instead of
generating potentially huge intermediate arrays. You start getting
answers sooner, you can write results out to disk or a socket without
buffering, and you can deal with infinite lists, but using the same
syntax.

e.g.
    a = (1..1/0.0).select{ |i| i % 2 == 0 }.map{ |i| i + 100
}.take(10).to_a

Implementing this turns out to be easier than you'd think, and is
efficient. No funky Fibers or Continuations required.

http://github.com/trans/facets/blob/master/lib/core/facets/denumerable.rb
http://github.com/trans/facets/blob/master/lib/core/facets/enumerable/defer.rb

···

--
Posted via http://www.ruby-forum.com/\.

And you base this ultimate conclusion on what?

Prior to Ruby 1.9, most Enumerable methods required that a block be
passed and would raise an error if not. There are few, such as to_a
which did not.

If you wanted an enumerator you needed to do something like

(1..100).enum_for(:inject, 0)

In Ruby 1.9 the enumerable methods got a little more relaxed, they
work as before if a block IS given, and if not they return an
enumerator instead.

So, my ultimate conclusion is that enumerable methods should almost
always use a block passed as an argument to do whatever they do, and
for Ruby 1.9 at least return an enumerator if a block is not passed
which will do whatever the enumerable itself would do with a block.

And a Ruby method can't distinguish wheter it was called with syntax like

   some_method { # some block
                        }

  or

   some_method(&something_which_understands_to_proc)

In both cases block_given? inside the method invocation will return true.

Now as I understand it, the proposal would be to make

  list.map(:method)

do the same thing as

list.map(&:method)

One problem with this is what to do with something like

list.inject(:initial_value)

Where instead of a proc, :initial_value represents the initial value
for an enumerator. I would argue that only the caller knows which one
he/she wants.

That "&" is pretty important in distinguishing these two cases.

···

On Tue, Nov 10, 2009 at 10:47 AM, Brian Candler <b.candler@pobox.com> wrote:

Roger Pack wrote:

we currently have
list.map => Enumerator

so should
list.map(:method)

best return an array or an enumerator?

The ultimate conclusion of this: all Enumerable methods should return
other Enumerators. When you want a real array, then add .to_a to the
end.

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Twitter: http://twitter.com/RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Rick Denatale wrote:

other Enumerators. When you want a real array, then add .to_a to the
end.

And you base this ultimate conclusion on what?

The OP was asking whether map(:foo) should return an Array or an
Enumerator. I was trying to say that if you're ambivalent about this,
one logical conclusion (or extreme viewpoint) is that you could always
return an Enumerator, even for

   map { |x| x*x }

I wasn't saying that Ruby does anything like this, and it's a tangent to
the original thrust of map(:foo).

···

On Tue, Nov 10, 2009 at 10:47 AM, Brian Candler <b.candler@pobox.com> > wrote:

--
Posted via http://www.ruby-forum.com/\.