On Thu, Mar 8, 2012 at 1:50 PM, Clement <cleme...@gmail.com> wrote:
> After a bit of searching through this group I was wondering if it's
> ever been discussed to make Strings iterable.
>
> I'm a Pythonista so I'm quite used to have convenient short-cuts like:
> for c in u'My Unicode string with あいうえお': print(c)
> Doing the same in Dart seems to be something like:
> for (var c in 'My Unicode String with あいうえお'.splitChars())
> { print(c); }
>
> What I'd love to be able to do is something like the Python example
> above. i.e.:
> for (var c in 'My Unicode String with あいうえお') { print(c); }
My experience with this in ruby was a mixed bag; people often want
iteration over characters, lines, or words, and it's not obvious which
is which.
I'd like to see properties that return lazy iterables (vs splitChars()
which splits eagerly). For example:
for (var c in "My unicode string".chars) {...}
for (var c in "My unicode string".words) {...}
for (var c in "My unicode string".lines) {...}
(On a more abstract level, I think this is just another form of
preferring aggregation over composition on the interface level, which
often makes APIs easier to understand).
> Secondly, while on the topic of iteration, I've noticed iterating over
> Maps is also a bit awkward.
>
> While I would usually do:
> myMap = {'a':1, 'b':2}
> for k in myMap: print(myMap[k])
>
> The only way to go in Dart seems to be:
> var myMap = {'a':1, 'b':2};
> myMap.forEach((k, v) => print(v));
>
> What I would like to do in Dart:
> var myMap = {'a':1, 'b':2};
> for (var k, v in myMap): print(v)
>
> I would argue that a less fragmented experience can be achieved with
> little cost on the compiler end by adopting some of these changes.
I actually prefer the other way around, I think iterating over Lists
should also be
list.forEach((v) => print(v));
The reason is that this mechanism is available to user code too, so
libraries can have their own control structures that look like part of
the language.
It also composes better: note that it can be written as:
list.forEach(print)
and your example is
myMap.values.forEach(print)
and is consistent with other operations that can make use of chaining:
myMap.values.transform((x) => 2*x).forEach(myList.add)
> In my opinion having to remember every single iteration convention for
> each type is something the developer shouldn't have to worry about.
I don't think that "m.forEach(k,v)" is any harder to remember than
"for (k,v in m)". In fact it's easier, because you can find the method
in the documentation for Map.
I think as with character iteration over strings, what seems obvious
is perhaps just familiar...
Cheers,
Sam
That said, if only we had lexical block return in Dart, then we could
really use methods for all control flow. So...why don't we again?
And by forEachCodePoint, do you mean like forEachByte or something?
I think the properties compose better, because forEach isn't the only
interesting thing to do with an iterator:
"quick brown fox".chars.transform((c) => rot13[c]).join('');
"to be or not to be".words.forEach((word) => frequency(word));
sourceCode.lines.filter((line) => line.length > 80);
novel.words.foldl(0, (x, y) => x + y.length) / novel.words.count();
and you don't want to have to define forEachChar, forEachWord,
forEachLine, transformChar, transformWord, transformLine, ...
Clement wrote:
> But I'm not quite sure if you want to get rid of the for..in and
> perhaps it's related Iterator class ?
Yeah, if I had my 'druthers:
* for..in would go away, replaced by the forEach method
* Iterator would stay, because...
* Iterable would gain some mixin methods. Any class implementing it
would get forEach, transform, count, foldl, foldr, filter.
* Map would look something like:
class Map<K,V> implements Iterable<Entry<K,V>> {
Collection<K> get() keys;
Collection<V> get() values;
}
class Entry<K,V> {
K get key();
V get value();
void set value(V newValue);
}
I think the properties compose better, because forEach isn't the only
interesting thing to do with an iterator:
(And yes, it's ambiguous - Ruby used to iterate over lines by default,
which is perfectly sensible for a language with Perl heritage that was
initially largely used for similar tasks. Eventually they deprecated
it in favor of each_line and each_char to remove the ambiguity. So
iterating characters will confuse at least rubyists and likely perl
hackers).
> Personally I find that one of the basic things I expect of any modern
> language is an ability to deal with strings and common operations on
> them. Especially any language aimed at the web.
> Iteration over strings is just one of those things that have cropped
> up often enough for me to expect it to be there.
Nobody's suggesting it shouldn't exist, just that it doesn't need to
live in a particular spot in the API just because python has it there
:-)
> While I love the idea of the forEach and friends (please, when can we
> have this?), I can't help thinking why make away with for..in now that
> it's there? I can easily see a world where the two co-exist.
There's a cost to having the for..in language feature (spec, parser,
compiler support, plus the complexity of learning the language) and
little to no benefit, IMO.
> I think code clarity might also be worth considering:
>
> for (var e in myVariable) {
> // code ..
> }
>
> myVariable.forEach((e) {
> // code ..
> });
>
> For simple loops like these the for..in feels cleaner and therefore
> more readable.
> When scanning a large piece of code the second you see "for .. " you
> know you've got a for loop followed by a block of code for the
> iteration.
> When you see the other one you first have to parse the variable, then
> you will have to assume it's of an iterable type because it has a
> forEach method, at which point you'd know the argument is a code block
> belonging to the iteration over the elements in the variable.
I think this is just familiarity - I can quickly infer the loop from
the indentation, it's much easier to find the data being operated on
when it's on the left before the dot (just like any other method
call).
Basically it's a wash, you've got exactly the same elements just in a
different order.
(Except for the extra closing paren after .forEach, I wish we could do
something about that).
Familiarity is worth considering, but bear in mind that in JS, this is
iteration:
list.forEach(function(x) { ... });
while this is something different (and usually wrong):
for (var x in list) { ... }
Given the positioning of Dart, familiarity to JSers is quite important.
Cheers,
Sam
Just wanted to pipe in and say that I definitely support moving away
from a for..in syntax to a forEach() function. My main reasoning being
that this allows other objects to implement this behaviour in the
future without special syntax support from the parser.
On another note, the Iterable mixin is the main reason I support
adding some form of mixins or default implementations to the language,
as I've found them invaluable in Ruby and JS (via extend()).