Philosophy and "for" loops — more from Go and Rust
lwn.net
lwn.net
https://github.com/mozilla/rust/wiki/Doc-detailed-release-no...
http://journal.stuffwithstuff.com/2013/01/13/iteration-insid...
The `for` construct will be changing in the 0.8 cycle to reflect this change. Currently it looks like:
for [1,2,3].each |i| { /* a closure */ }
Where eventually it might look something like: for i in [1,2,3] { /* just a regular block */ }
I'm actually surprised that the author praises the "freedom and flexibility" of our old `for` loops... the reason for making this switch at all is because we found our old semantics to be neither sufficiently composable nor sufficiently flexible! :)Hopefully the author will revisit this space once the new implementation is finalized, perhaps with a comparison to D this time.
TL;DR: Rust is switching idioms from Ruby-style iteration to C#-style iteration because (for our purposes) it's more performant, easier to optimize, more composable, and more flexible.
for [1, 2, 3].iter().advance |i| { /* a closure */ }https://github.com/mozilla/rust/blob/master/src/libstd/itera...
Edit: In fact, this is true of any language with higher-order functions. For example, this is how you'd define a Rust-style internal iterator for lists in Racket scheme:
(define (iter l f)
(when (and (pair? l) (f (car l)))
(iter (cdr l) f)))
And how you'd use it: ;; displays elements of the list, up to and including the first one >= 3
(iter '(1 2 3 4) (lambda (x) (display x) (< x 3))) do tree.visit_each_leaf |leaf| {
// ... do things with leaf ...
if some_continuation_condition { true } else { false }
}
(The last line is the replacement for what would be currently written `if !some_continuation_condition { break }` with the `for` sugar.)Having said that, I thought the Go `readLine` using channels example was strange. The goroutine that provides lines to the channel actually still needs to loop over the lines, and in fact includes a DRY version of the loop in question:
for {
line, ok := file.readLine()
if !ok { break }
// ...
}
This is not to say that using an unguarded loop with a break is necessarily the best way to do this, but it seems to be the simplest DRY way to do this. I think he just wanted an excuse to demonstrate `range` with a channel.http://common-lisp.net/project/iterate/doc/index.html
ITERATE is more powerful than LOOP and extensible.
"one often has to consult the manual to recall lesser-used aspects of the strange syntax"
Guess where my copy of CLTL2 falls open at?
[Unfortunately, it's a long time since I did much CL development]
Also even within CL, there are lots of people who dislike it. Also this is just nice suger, the article was really about using diffrent datatypes.
When you only provide one variable for assignment, you are actually assigning the key (or incremental value), not the value, the syntax is:
for key, value: := range someThing {
When you do: for something := range someThing {
something won't have the value. If you just want the value you need to use: for _, value := range someThing {that's close to true but not strictly true, since you can define your own types using slices, maps and channels. The reason you would do that is that by defining your own type, you can give a slice, map, or a channel type a method set, which by extension means that you can create a slice, map, or channel that satisfies an interface. E.g.: http://play.golang.org/p/04IBK5OwNk
>While interesting new control flow is unlikely to be a headline item on a newly developed language these days
...ummm, goroutines? I know that's not "new" in the sense that CSP has been around since the 70's, but the `go` keyword is a new control flow mechanism for many programmers. Concurrency is a control flow concept.
Couldn't agree more. Also a list of all flow control constructs that doesn't include destructuring pattern match ain't a complete list in my book. if and switch are just special degenerate cases of pattern-matching.
I haven't used Rust so I don't know how its done in Rust but it looks promising can't wait to play around with it as well.
The reason there are multiple ways of doing things in nearly all languages is that people have found that not all common use cases fall nicely into one construct. A language can either be plauged by everyone reimplementing some common constructs or it can add some of those constructs as "another way of doing things".
I don't really get this thread at all actually. Most of the article was about how Go actually has three looping constructs, so the entire premise seems ...off.
(Or syntactic sugar for map/foreach)
Otherwise well written and enjoyable.
for (;;) {}
djb uses this a lot.
int c; for(c=0; str[c] != '\0'; c++) {} printf("Length of string = %d\n", c);
C doesn't have a message or a worldview. It's just about saving typing and making common things convenient.
Ah well. This too shall pass.
The problem being, as you point out, that it imposes a fixed order, regardless of whether the problem domain requires such an order.
In database programming one of the biggest challenges for beginners is to stop thinking in terms of loops; to learn to "think in sets".
But, honestly, I could not articulate the problem as you have. I wish I had your knowledge. The idea of a "jump" really captures it. setjmp. goto. It's navigating memory. And nothing more.
I can visualize the problem, sort of - I just intuitively feel like sets, Lisp-like lists, and vector-based approaches, are the way to go in high level programming - but I do not know the right words to describe and argue my point.
As such, I doubt I could convince many high level programmers to abandon the use of iterations in the code they write.
Again, you have beautifully articulated what I could not. Cheers.
Even C blurs the model (which itself is a lie these days, but no matter). It didn't matter so much when C was created, because early C programmers were also assembler programmers. These days that's no longer true.
Sorry, I could not resist.
(loop for i from 1 to 10 collecting i)
Other would say "use recursion".)