=20
I completely disagree. "item for item in items if item"? How is
that easy to understand?
Ummm, maybe a closer translation would be:
"computation for item in collection if condition"
Naming computation/item/collection/condition the same and then
declaring unreadability is a straw man.
Given the translation above, consider the following english comment of
what is actually happening:
"do computation for each item in collection with condition"
The "do" and "each" are implicit. That's fine with me, the language
shouldn't read as full english or it will be too verbose. The only
other change is replacing "if" by "with". It be nice if the list
comprehension used "with" instead:
manager_salaries =3D [e.salary for e in employees with e.manager?]
vs.
manager_salaries =3D [e.salary for e in employees if e.manager?]
Depends on who you are. Me? I like "with" better, but can live with
"if" just fine. I think the main readability argument for list
comprehensions is that the flow of the construct is similar to the
flow of the english sentence that describes it. Ok, is a
select/collect implementation less readable?
manager_salaries =3D employees.
select{ |e| e.manager? }.
collect{ |e| e.salary }
English translation:
"For each item in collection with condition, do computation"
NO! An argument for readability of list comprehensions is not an
argument against readability of select/each/collect etc. I love thems,
and I want to keep thems!
Adding list comprehensions wouldn't imply
removal of the equally readable and powerful block constructs we're
used to.
However, notice one thing about the select/collect implementation
above. We iterate through employees once and create a temporary array.
We then iterate through that temporary array again during the collect.
That seems a bit inefficient to me. The list comprehension -- with
equal readability -- removes that inefficiency.
Does Ruby need list comprehensions to solve that? Not necessarily:
module Enumerable
def collect_filtered(filter: filter, mapping: mapping)
self.inject([]) { |c,e| filter[e] ? c << mapping[e] : c }
end
end
manager_salaries =3D employees.collect_filtered(
filter: -> (e) { e.manager? },
mapping: -> (e) { e.salary }
)
I've opted to use the proposed 2.0 syntax for lambdas for consistency
with the assumption of named parameters. This would be much less
readable without the named parameters. This solves the efficiency
problem, but does it really retain the readability of either the
inefficient select/collect implementation or the list comprehension?
I, personally, don't think so.
I hope you've enjoyed this little detour through my thought process...
Jacob Fugal