From: sammomichael@... Date: 2019-09-06T16:41:43+00:00 Subject: [ruby-core:94804] [Ruby master Feature#16147] List Comprehensions in Ruby Issue #16147 has been updated by sammomichael (Samuel Michael). Backport deleted (2.5: UNKNOWN, 2.6: UNKNOWN) Tracker changed from Bug to Feature shevegen (Robert A. Heiler) wrote: > I am not sure how serious the proposal is; but I will assume, for sake of simplicity, that > the suggestion is "real". First a minor comment - it should be filed under "features" > rather than "bug", since it is a suggestion for a change, rather than a bug, in my > opinion; but this is an aside. > > Next, I will briefly comment on this statement: > > > Ruby has a for...in loop that is rarely used but could possibly be repurposed. > > Let's for a moment briefly ignore whether people use the for loop or not (they > actually do; but let's ignore this for the moment). > > Matz has said several times before that the transition from ruby 2.0 (2.x) to > 3.0 will not be anywhere near as "problematic" as 1.8.x to 2.x was. So for > this reason alone, I believe that IF this were to be approved, it would have > to come way after 3.0, possibly 3.1 at earliest or even after that - so I > guess a few years from this point. (Ruby 3.0 will be released next year, > so we don't have that much time really; 15 months or so, give or take.) > > You also wrote that the "for" loop is rarely used. Well, this is partially > true. I myself use almost exclusively .each and loop {}. But - I know of > other people who use "for" quite a bit, in particular when you have like > a matrix and iterate through it; in this case I can understand that a for > loop is used, even if I personally prefer .each and loop {}. So I am not > sure if your general comment is correct. > > I assume that one reason why a for loop exists, and also other aliases, > such as .map <- -> .collect, and as to why ruby is multi-paradigm, was > that matz wanted to make ruby convenient to use for people with different > background. Ruby itself has incorporated useful ideas and paradigms from > other languages too. (My personal opinion is that, by far, the strongest > point of ruby, in regards to philosophies, is its OOP-centric nature, but > you may disagree. People use ruby in different ways, though. Some of the > ruby code looks quite alien; and matz has said sometimes, in the past, > that he was sometimes surprised to see how people use ruby.) > > The philosophy of "more than one way to do it" also means that the "lesser > ways", that is, used more sporadically, will not necessarily be removed > merely because most ruby users may not use it. There are many other examples > here, such as @@class_variables. I don't use the latter myself, and would > rather see them go, but there are others who use class variables just fine, > even in the ruby code base (the recent rewrite of irb for example has some > class variables). So I think this is not a good metric in regards to > "adoption". But this is also not the main issue here. > > The next comment to make .... hmm. List comprehensions remind me of python. > > I use both ruby and python fine, but python feels a bit strange, in many > ways. Including python's OOP way (I absolutely hate explicit self by far > the most; mandatory indent is so minor compared to having to pass self > to every function/method in a class). IMO, ruby's way to filter is a > LOT more natural and easier to both understand and read than are python's > list comprehensions. > > Granted, you were not confining your suggestion to python alone, so there > may be other list comprehensions that are succinct and elegant. I don't > really see them in the proposal per se, but perhaps they exist; I don't > know. I think that ruby's existing ways are very succinct, though. > Ultimately I don't think ruby needs list comprehensions, most definitely > not the pythonic list comprehensions. > > You gave an example as-is in ruby: > > S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1] # [0, 4, 8, 12, 16] > > Off the top of my mind, this would be better, in my opinion: > > (0..16).select {|entry| entry % 4 == 0} => [0, 4, 8, 12, 16] > > I am sure there are many other ways (Dan0042 provided more examples), > but IMO, the latter is so much more readable than the first variant, > so I don't think there is any real improvement. > > You gave more examples such as: > > c = -> x do $*.clear > if x['if'] && x[0] != 'f' . > > I don't really know what this is. It almost looks as if there was an > attempt made to make this less readable. > > Anyway you only have to convince matz, but I am not sure if the > proposal as it presently is has a good chance for implementation; > there seem to be too many trade offs associated with it in its > current form. Or perhaps there is some additional reasoning or > advantage missing; I am not quite seeing the improvement or the > stated need, to be honest. > > Best luck to you nonetheless. There is nothing wrong with the current ruby syntax of using enumerables such as map/select/filter_map to generate the same type of list. But as you said ruby is all about many ways of doing things. And as many languages offer some version of this feature (Julia, Python, CoffeeScript, etc.) it could be beneficial for ruby to incorporate its own variation. There is something natural and refreshing about using pure mathematical set builder notation with keywords for...in to generate an array.`[*1..10].filter_map{@1**2 if @1.even?} vs [x**2 for x in 1..10 if x.even?]` The second version has less symbols, less keystrokes and is more human readable for beginners imo. Also I didn't mean to suggest that we retire the normal for...in loop (which I use myself) to break older code or that nobody ever uses it, but I am suggesting that we extend it if possible, just as was done with making "in" the keyword for the new pattern matching system. [[https://en.wikipedia.org/wiki/Comparison_of_programming_languages_(list_comprehension)]] Dan0042 (Daniel DeLorme) wrote: > What's wrong with existing well-established object-oriented ruby idioms? > > ```ruby > (1..10).to_a > (1..10).select(&:even?) > (1..10).select(&:even?).map{ |x| x**2 } > (1..10).map{ |x| x**2 } > ``` > > or in ruby 2.7: > > ```ruby > (1..10).to_a > (1..10).select(&:even?) > (1..10).select(&:even?).map{ _0**2 } > (1..10).map{ _0**2 } > ``` > > or if #16120 was accepted ;-D > > ```ruby > (1..10).to_a > (1..10).select{.even?} > (1..10).select{.even?}.map{.**2} > (1..10).map{.**2} > ``` ---------------------------------------- Feature #16147: List Comprehensions in Ruby https://bugs.ruby-lang.org/issues/16147#change-81426 * Author: sammomichael (Samuel Michael) * Status: Open * Priority: Normal * Assignee: * Target version: ---------------------------------------- ## List comprehensions are present in many languages and programmers are quite fond of their simplicity and power. Add to that the fact that Ruby has a for...in loop that is rarely used but could possibly be repurposed. ### Currently we can already do a hack like this to make Ruby support list comprehension syntax: ``` ruby S = [for x in 0...9 do $* << x*2 if x.even? end, $*][1] # [0, 4, 8, 12, 16] ``` Still, it would be far nicer if the for...in loop would return the desired array automatically, this is one way to approach that taking advantage of lambda bracket invocation syntax: ``` ruby c = -> x do $*.clear if x['if'] && x[0] != 'f' . y = x[0...x.index('for')] x = x[x.index('for')..-1] (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}") x.insert(x.length, "end; $*") eval(x) $*) elsif x['if'] && x[0] == 'f' (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << x") x.insert(x.length, "end; $*") eval(x) $*) elsif !x['if'] && x[0] != 'f' y = x[0...x.index('for')] x = x[x.index('for')..-1] (x.insert(x.index(x.split[3]) + x.split[3].length, " do $* << #{y}") x.insert(x.length, "end; $*") eval(x) $*) else eval(x.split[3]).to_a end end ``` so basically we are converting a string to proper ruby syntax for loop then we can use python syntax in a string to do: ``` ruby c['for x in 1..10'] c['for x in 1..10 if x.even?'] c['x**2 for x in 1..10 if x.even?'] c['x**2 for x in 1..10'] # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] # [2, 4, 6, 8, 10] # [4, 16, 36, 64, 100] # [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] ``` -- https://bugs.ruby-lang.org/ Unsubscribe: