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/
on 08.11.2009 01:22
on 08.11.2009 01:43
> list.map :name
Makes sense, given that you can already do
list.reduce(:+)
Tor Erik
on 08.11.2009 01:57
Hi -- On Sun, 8 Nov 2009, Roger Pack wrote: > > 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 -- 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)
on 08.11.2009 02:01
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).) I garbled that bit, due to an irb session gone horribly wrong :-) 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 -- 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)
on 08.11.2009 07:05
On Nov 8, 11:57 am, "David A. Black" <dbl...@rubypal.com> wrote: > > 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. 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 08.11.2009 15:40
Hi -- On Sun, 8 Nov 2009, Gavin Sinclair wrote: >> object will be converted to a Proc and play the block role. > 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 :-) 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 -- 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)
on 08.11.2009 18:56
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! -- 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
on 08.11.2009 22:49
Hi -- On Mon, 9 Nov 2009, Rick DeNatale wrote: > > > 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 :-) 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)
on 09.11.2009 16:36
>> 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
on 09.11.2009 16:49
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
on 10.11.2009 04:05
list.map(:symbol) is just like object.send(:symbol),except the :symbol for list. so list.map(:symbol) is good~
on 10.11.2009 04:41
Hi -- On Tue, 10 Nov 2009, Roger Pack wrote: > 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 :-) That's always the flip-side of the precedent reasoning. 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)
on 10.11.2009 05:57
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
on 10.11.2009 10:36
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.
on 10.11.2009 16:05
>> 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
on 10.11.2009 16:25
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
on 10.11.2009 16:47
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
on 10.11.2009 20:46
On Tue, Nov 10, 2009 at 10:47 AM, Brian Candler <b.candler@pobox.com> wrote: > other Enumerators. When you want a real array, then add .to_a to the > end. 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. -- 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
on 10.11.2009 21:30
Rick Denatale wrote: > On Tue, Nov 10, 2009 at 10:47 AM, Brian Candler <b.candler@pobox.com> > 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 11.11.2009 06:25
Brian Candler wrote: > 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). That's a pretty yucky option. I use map all the time, and I expect to get an array back. Adding to_a everywhere is not cool. Lazy evaluation is cool, though. But it would need a thorough reconsideration of the language. And considering that Ruby is doing pretty well without it...
on 11.11.2009 13:13
Hi -- On Wed, 11 Nov 2009, Brian Candler wrote: > 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). This reminds me of the early 1.9.0 thing, where you could do: array = [1,2,3,4] enum = array.enum_for(:map, &lambda {|x| x * x }) enum.next # 1 enum.next # 4 etc. (That's ruby 1.9.0 (2008-03-01 revision 15660) [i686-darwin9.2.0].) To be honest, when that disappeared, it seemed to me to do away with a great deal of the usefulness of enumerators. Given that you can no longer attach a block to an enumerator when you create the enumerator, I don't think there are any actual use cases for the fact that map returns an enumerator (or at least very, very few). You can't do this, for example (following the above example): enum.select {|x| x > 1 } # [4, 9, 16] 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)
on 13.11.2009 17:22
Hi,
I'm quite conservative or maybe 'wise', seeing this discussion.
In fact, using arr.map(:method, arg1 ... ) is too confusing to me
While the &:method notation is clear, and not that ugly.
And I think it has to be a separation between blocks and arguments;
block is
another "kind" of argument.
That's why I think we should keep the &:method, and forget about
:method.
Note also that is not a common use of &:method, because you often need
to do
ore than one thing with the element. eg:
[1,15,3].map { |e| e.to_s.length }
P.S.:
Why &:method is faster ? (Bad, I wanted to argue &:method is slower but
I
can't ...)
(requiring and including Benchmark)
irb(main):008:0> realtime { (1..1000000).to_a.map &:to_s }
=> 0.38111090660095215
irb(main):009:0> realtime { (1..1000000).to_a.map { |e| e.to_s } }
=> 0.445728063583374
2009/11/11 David A. Black <dblack@rubypal.com>
on 13.11.2009 18:30
> This reminds me of the early 1.9.0 thing, where you could do: > > array = [1,2,3,4] > enum = array.enum_for(:map, &lambda {|x| x * x }) > > enum.next # 1 > enum.next # 4 It is surprising that currently it appears to *ignore* the given block. >> b = File.open('file', 'r').lines{|line| lines * 2} >> b.next => "xx\n" >>> b.next => "xx\n" Is this expected? -r
on 13.11.2009 19:42
>This reminds me of the early 1.9.0 thing, where you could do: > > array = [1,2,3,4] > enum = array.enum_for(:map, &lambda {|x| x * x }) > > enum.next # 1 > enum.next # 4 Appears somewhat possible... class Enumerator def filter(&blk) self.class.new do |y| each do |*input| out = blk.call(*input) y << out # or y << out if out end end end end >> array = [1,2,3,4] >> enum = array.enum_for(:map).filter {|x| x * x } >> enum.next => 1 >> enum.next => 4 >> enum.select {|x| x > 1 } # [4, 9, 16] => [4, 9, 16] [Thanks to Brian Candler for the snippet--though perhaps it could be improved...] This should be the default, though--I don't know why blocks are ignored currently... -r http://www.ruby-forum.com/topic/169844#new