Purity of delegate-style toString

Q. Schroll qs.il.paperinik at gmail.com
Sun May 6 17:39:58 UTC 2018


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!"..."(...);
>>      }
>> 
>> pure?
>
> 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("...");
>         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).)


More information about the Digitalmars-d-learn mailing list