--20cf307c9f28455ee104b25bd8a0 Content-Type: text/plain; charset=ISO-8859-1 I am nervous about list comprehensions because in almost all cases, when you do something in Ruby, you do it by invoking a named method on an object. It is clear by looking at a piece of code which named method will be invoked. In Ruby 1.9, it is even trivial to learn the exact source location of such a named method. In contrast, list comprehensions introduce new syntax that invokes some invisible protocol; understanding which methods are involved requires figuring out where to look in the documentation. Python uses protocols like this for *everything*, so list comprehensions fit in well there. The only Ruby case I can think of that works like this is the much maligned for/in syntax, which invokes #each under the hood. Most people I know find this strange, I suspect because of its inconsistency with the very strong rule that if a method is directly invoked by some syntax, you can see it. If you want to see where this protocol-oriented path leads us, check out http://docs.python.org/reference/datamodel.html#special-method-names. I much prefer Ruby's "if you want to do something, invoke a named method" principle of uniform access[1]. [1] http://en.wikipedia.org/wiki/Uniform_access_principle Yehuda Katz (ph) 718.877.1325 On Tue, Nov 22, 2011 at 2:26 PM, Shugo Maeda <shugo / ruby-lang.org> wrote: > Hi, > > 2011/11/23 Yehuda Katz <wycats / gmail.com>: > > It is pretty common to want to map over an Enumerable, but only include > the elements that match a particular filter. A common idiom is: > > > > enum.map { |i| i + 1 if i.even? }.compact > > > > It is of course also possible to do this with two calls: > > > > enum.select { |i| i.even? }.map { |i| i + 1 } > > > > Both cases are clumsy and require two iterations through the loop. I'd > like to propose a combined method: > > > > enum.map_select { |i| i + 1 if i.even? } > > > > The only caveat is that it would be impossible to intentionally return > nil here; suggestions welcome. The naming is also a strawman; feel free to > propose something better. > > How about to add list comprehensions or Scala's for expressions instead? > For example, enum.select { |i| i.even? }.map { |i| i + 1 } can be > written as follows: > > [ i + 1 for i in enum if i.even? ] > > # The syntax of list comprehensions needs more considerations. > > One benefit is that nested maps can be flattened by list comprehensions. > For example, the following code: > > pyths [x, y, z] for z in [1..Float::INFINITY].defer > x in [1..z].defer > y in [x..z].defer > if x**2 + y**2 z**2 ] > p pyths.take(3) > > is equivalent to the following code: > > pyths 1..Float::INFINITY).defer.flat_map {|z| > (1..z).defer.flat_map {|x| > (x..z).defer.select {|y| > x**2 + y**2 z**2 > }.map {|y| > [x, y, z] > } > } > } > p pyths.take(3) > > # Enumerable#defer is proposed in Feature #4890. > > BTW, now Ruby has map and reduce as aliases of collect and inject, > but not filter as an alias of select. Why not? > > -- > Shugo Maeda > > --20cf307c9f28455ee104b25bd8a0 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable I am nervous about list comprehensions because in almost all cases, when you do something in Ruby, you do it by invoking a named method on an object. It is clear by looking at a piece of code which named method will be invoked. In Ruby 1.9, it is even trivial to learn the exact source location of such a named method.<div> <br></div><div>In contrast, list comprehensions introduce new syntax that invokes some invisible protocol; understanding which methods are involved requires figuring out where to look in the documentation.</div><div><br> </div> <div>Python uses protocols like this for *everything*, so list comprehensions fit in well there. The only Ruby case I can think of that works like this is the much maligned for/in syntax, which invokes #each under the hood. Most people I know find this strange, I suspect because of its inconsistencyith the very strong rule that if a method is directly invoked by some syntax, you can see it.</div> <div><br></div><div>If you want to see where this protocol-oriented path leads us, check outa href="http://docs.python.org/reference/datamodel.html#special-method-names">http://docs.python.org/reference/datamodel.html#special-method-names</a>. I much prefer Ruby's "if you want to do something, invoke a named method" principle of uniform access[1].</div> <div><br></div><div>[1]a href="http://en.wikipedia.org/wiki/Uniform_access_principle">http://en.wikipedia.org/wiki/Uniform_access_principle</a></div><div><br clear="all">Yehuda Katz<br>(ph) 718.877.1325<br> <br><br><div class="gmail_quote">On Tue, Nov 22, 2011 at 2:26 PM, Shugo Maeda <span dir="ltr"><shugo / ruby-lang.org></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;"> Hi,<br> <br> 2011/11/23 Yehuda Katz <wycats / gmail.com>:<br> <div class="im">> It is pretty common to want to map over an Enumerable, but only include the elements that match a particular filter. A common idiom is:<br> ><br> > enum.map { |i| i + 1 if i.even? }.compact<br> ><br> > It is of course also possible to do this with two calls:<br> ><br> > enum.select { |i| i.even? }.map { |i| i + 1 }<br> ><br> > Both cases are clumsy and require two iterations through the loop. I'd like to propose a combined method:<br> ><br> > enum.map_select { |i| i + 1 if i.even? }<br> ><br> > The only caveat is that it would be impossible to intentionally returnil here; suggestions welcome. The naming is also a strawman; feel free toropose something better.<br> <br> </div>How about to add list comprehensions or Scala's for expressions instead?<br> For example, enum.select { |i| i.even? }.map { |i| i + 1 } can be<br> written as follows:<br> <br> ¨Â æïéî åîõí éæ é®åöåîݼâò<br> # The syntax of list comprehensions needs more considerations.<br> <br> One benefit is that nested maps can be flattened by list comprehensions.<br> For example, the following code:<br> <br> ¨Âùôèó Ûøù¬ úÝ æïéî Û±®®ÆìïáôººÉÎÆÉÎÉÔÙÝ®äåæåò¼âò¾ ¨Â éî Û±®®úÝ®äåæåò¼âò¾ ¨Â éî Ûø®®úÝ®äåæåò¼âò¾ ¨Âøªª² ùªª² ½½ úªª² ݼâò ¨Â ðùôèó®ôáë娳©¼âò<br> is equivalent to the following code:<br> <br> ¨Âùôèó ¨±®®ÆìïáôººÉÎÆÉÎÉÔÙ©®äåæåò®æìáôßíáûüúü¼âò¾ 1..z).defer.flat_map {|x|<br> x..z).defer.select {|y|<br> ¨Âªªùªª² ½½ úªª²¼âò¾ ¨Â®íáð ûüùü¼âò¾ ¨Âø¬ ù¬ úݼâò¾ ¨Â¼âò¾ ¨Â¼âò¾ ¨Â¼âò¾ ¨Â ðùôèó®ôáë娳©¼âò<br> # Enumerable#defer is proposed in Feature #4890.<br> <br> BTW, now Ruby has map and reduce as aliases of collect and inject,<br> but not filter as an alias of select. ¨Âèù îïô¿¼âò¾ <span class="HOEnZb"><font color="#888888"><br> --<br> Shugo Maeda<br> <br> </font></span></blockquote></div><br></div> --20cf307c9f28455ee104b25bd8a0--