This sounds fine. The only downside I can see to Marijn's technique is that there is no way for an iteration function to distinguish "broke out of the loop" from "returned out of the loop". This may or may not be important? I could imagine that the iterator may want to perform actions on a break which conceptually follow the loop but not perform those actions on a return, but I can't actually come up with any convincing examples.

As far as nesting, I think you can do it in various ways, but as long as we're going to build for loops into the language it makes sense to do nesting the more efficient way that you describe: that is, the outer for loop creates a location to store to and inner for loops all refer to that same location.

Just for reference, though, the naive translation of a nested loop pair works fine as long as you translate ret consistently---outside of a for loop, it returns from the method, inside it stores to the innermost temp and breaks. In that case, the inner for loop expands in the first round to:

    for i in a {
        let tmp2 = none;
b.iter { |j| tmp2 = some(...); ret false; } // <-- this is a "native" return
        if tmp2 != none {
ret option::unwrap(tmp2); // <-- this return must still be desugared
        }
    }

now when we de-sugar the outer loop we get:

    let tmp = none;
    a.iter { |i|
        let tmp2 = none;
b.iter { |j| tmp2 = some(...); ret false; } // <-- this is a "native" return
        if tmp2 != none {
            tmp = some(option::unwrap(tmp2));
            ret false; // <-- this is a "native" return
        }
     }
     if tmp != none {
        ret option::unwrap(tmp); // <-- this is a native return
        // as there are no more for loops
     }

Of course, here you end up with two temporaries where only one is needed so it makes more sense to be smart about it. But it's good to know that the translation does compose: I'd be worried otherwise.


Niko

On 2/29/12 2:01 PM, Graydon Hoare wrote:
On 12-02-29 01:12 PM, Marijn Haverbeke wrote:
I was actually thinking there wouldn't have to be a special may_return
type at all. The returned value can be an option, and return can store
to that and then break. Code after the call to the iterator then
checks for a `some` value in the return slot and, if found, returns
it.

This is coming into focus a little better. I like where it's going. If we make these two assumptions though:

  - Repurpose the 'for' loop to avoid boilerplate
  - Store-into-option to avoid a polymorphic tag

Can we not, again, just get away with 'bool'? That is:

for i in v(z) { ret q; }

becomes:

{
    let tmp = none;
    v.iter(z) {|i| tmp = some(q); ret false; }
    alt tmp {
        some(q) { ret q; }
        _ { }
    }
}

and the iter can be written pleasantly as:

fn iter(self: [int], x: foo, f: fn(int) -> bool) {
   let i = 0;
   while f(i) { i += 1; }
}

Two questions: is this tolerable, and does it nest properly?

I think it's tolerable, personally. I'd appreciate hearing other opinions on that.

Meanwhile let's look at nesting...

for i in a {
    for j in b {
        ret q;
    }
}

does not work if the nesting translates, naively, that is, if it becomes:

let tmp1 = none;
a.iter() {|i|
    let tmp2 = none;
    b.iter() {|j|
        tmp2 = some(q); ret false;
    }
    alt tmp2 {
        some(q) { ret q; }
        _ {}
    }
}
alt tmp {
    some(q) { ret q; }
    _ {}
}

This is a bad translation -- won't even compile -- since the inner block winds up with the wrong type. And anyways control flow doesn't propagate right. I think there's a right translation though. If it instead becomes:

let tmp = none;
a.iter() {|i|
    b.iter() {|J|
        tmp = some(q); ret false;
    }
    if tmp != none { ret false; }
}
alt tmp {
    some(q) { ret q; }
    _ {}
}

Then I think it's ok. IOW there's subtlety to the translation, it has to be nesting aware. In particular:

  - There's only _one_, outermost option to write to. All early rets
    write to it.
  - Any nested for loop has to inspect the outer option at its end,
    and ret false to break its enclosing loop, if the option changed to
    'some'.

I _think_ that's enough to make the translation work. Anyone see holes in it? I'm definitely interested in "getting this right", it's just somewhat ... subtle.

-Graydon

_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to