[Issue 5660] New: yield syntax sugar

d-bugmail at puremagic.com d-bugmail at puremagic.com
Sun Feb 27 17:58:18 PST 2011


http://d.puremagic.com/issues/show_bug.cgi?id=5660

           Summary: yield syntax sugar
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody at puremagic.com
        ReportedBy: bearophile_hugs at eml.cc


--- Comment #0 from bearophile_hugs at eml.cc 2011-02-27 17:55:27 PST ---
Ranges are flexible and useful, but my practice with Python has shown me that
many times you just want something simple that yields items lazily. To do this
in D there is opApply() (that currently doesn't play well with most Phobos.
Only std.array.array() and few other things are able to use them), but its
syntax is awful, and even after years of usage I can't remember it and I need
to look for an example of opApply() usage to copy & modify.

opApply() requires a significant amount of boilerplate code that makes code
quite longer, obfuscates the purpose of the code, and is bug-prone. This is a
simple example (D2 code, works with dmd 2.052):


// Program #1
import std.stdio;

/// Sequence of moves to solve Towers of Hanoi OEIS A001511
immutable final class hanoiTower {
    static opCall() {
        return new typeof(this)();
    }

    int opApply(int delegate(ref int) dg) {
        int result;
        int y = 1;

        result = dg(y);
        if (result)
            return result;

        foreach (x; hanoiTower()) {
            y = x + 1;
            result = dg(y);
            if (result)
                return result;

            y = 1;
            result = dg(y);
            if (result)
                return result;
        }

        return result;
    }

    int opApply(int delegate(ref int, ref int) dg) {
        int result;
        int i, y = 1;

        result = dg(i, y);
        i++;
        if (result)
            return result;

        foreach (x; hanoiTower()) {
            y = x + 1;
            result = dg(i, y);
            i++;
            if (result)
                return result;

            y = 1;
            result = dg(i, y);
            i++;
            if (result)
                return result;
        }

        return result;
    }
}


class genChar {
    char stop;

    this(char stop_) {
        stop = stop_;
    }

    static opCall(char stop_) {
        return new typeof(this)(stop_);
    }

    int opApply(int delegate(ref char) dg) {
        int result;
        for (char c = 'a'; c < stop; c++) {
            result = dg(c);
            if (result)
                return result;
        }
        return result;
    }

    int opApply(int delegate(ref int, ref char) dg) {
        int result, i;
        for (char c = 'a'; c < stop; c++) {
            result = dg(i, c);
            i++;
            if (result)
                return result;
        }
        return result;
    }
}

void main() {
    foreach (i, move; hanoiTower()) {
        writeln(i, " ", move);
        if (i >= 20)
            break;
    }
    writeln();

    foreach (i, c; genChar('h'))
        writeln(i, " ", c);
}


So I suggest a syntax like this, that's just syntax sugar for the precedent
code:

// Program #2
import std.stdio;

yield(int) hanoiTower() {
    yield(1);
    foreach (x; hanoiTower()) {
        yield(x + 1);
        yield(1);
    }
}

yield(auto) genChar(char stop) {
    for (char c = 'a'; c < stop; c++)
        yield(c);
}

void main() {
    foreach (i, move; hanoiTower()) {
        writeln(i, " ", move);
        if (i >= 20)
            break;
    }
    writeln();

    foreach (i, c; genChar('h'))
        writeln(i, " ", c);
}


Different good modern used languages like Python, C# and Scala have a clean
yield syntax, they show the way (if necessary I may translate this program #2
to those other three languages.)

The normal syntax of opApply() is of course kept in D, this is just an additive
change, that acts at syntax level (so just a lowering is needed to implement
it).

I suggest the generators with yield to be classes that define a "static
opCall()" because their semantics is more flexible than structs. This causes a
heap allocation, but situations where performance is so important are less
common, and in such situation the programmer may just write the normal struct
code with opApply(), or use a struct with more inlinable range protocol
methods, or use something more lower level.

The syntax uses yield(...) as return type to denote such iterable classes, and
yield(auto) too supported, and it acts like auto return type for functions (all
yields in a generator must yield the same type).

Some disadvantages:
- "yield" may need to become a kind of keyword.
- Currently opApply() doesn't work well with std.algorithm and Phobos in
general. This is true for the normal current usage of opApply(), but this
proposal makes their usage more attractive and probably more widespread.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------


More information about the Digitalmars-d-bugs mailing list