On Tuesday, 1 May 2018 at 12:03:15 UTC, ag0aep6g wrote:
On 05/01/2018 01:44 PM, Per Nordlöw wrote:
In which cases (if any) is it possible to make a delegate-style implementation of toString such as

    void toString(scope void delegate(const(char)[]) sink) const @trusted pure
         // sink("...");
         // sink.formattedWrite!"..."(...);


You have to mark `sink` as pure, too:

    void toString(scope void delegate (const(char)[]) pure sink)
        const @trusted pure

Then the toString method itself works, but it may not be callable by other code that wants to use an impure sink.

For example, `format` works, but `writeln` doesn't:

struct S
    void toString(scope void delegate(const(char)[]) pure sink)
        const @trusted pure
        import std.format: formattedWrite;
        sink.formattedWrite!"%s"(" ...");
void main()
    import std.format: format;
    import std.stdio: writeln;
    writeln(format("%s", S())); /* Ok. Prints "... ...". */
writeln(S()); /* Nope. writeln would like to use an impure sink. */

By the way, you shouldn't mark toString as @trusted when `sink` is @system.

I had similar issue for opApply.

The generalized problem is, the attributes (pure, nothrow, @safe, @nogc) are too strong on functionals (i.e. functions taking function/delegate arguments). We could (and IMO should) weaken the attributes to mean: the same as always, *assuming all function/delegate arguments have it*.

Concrete example, say your `toString(scope void delegate(const(char)[]))` is conceptually pure, i.e. if `sink` is a pure function (by static typing), `toString(sink)` acts pure, and for impure `sink`, `toString(sink)` possibly impure. So purity of the functional `toString` depends on the purity of its arguments; that is very natural as most functionals call their parameters.

The current state makes attributes virtually useless for functionals. Often, toString can be templetized without drawback (except virtual functions), but opApply cannot. opApply must not be a template to enable type deduction for the variable.[1]

Making attributes act structurally has almost no consequences in terms of breakage; just more functions can be pure/nothrow/@nogc/@safe. It would make functionals impure/unsafe/.. that do not call their argument. The question is, in which contexts are they used and is it an issue -- is it a greater issue than this.

Complete example what the change would do:
Say you have

void toString(scope void delegate(const(char)[]) sink) pure { sink("Example"); }

toString is a pure functional, so calling it is pure iff the argument itself is. The compiler statically knows if the argument is pure and can infer the purity of the expression.

[1] https://dlang.org/spec/statement.html#foreach_over_struct_and_classes (We could define very general special cases where type deduction can be archived, e.g. opApply(DG : int delegate(Args))(DG).)

Reply via email to