www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Purity of delegate-style toString

reply Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
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?
May 01 2018
parent reply ag0aep6g <anonymous example.com> writes:
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.
May 01 2018
next sibling parent Per =?UTF-8?B?Tm9yZGzDtnc=?= <per.nordlow gmail.com> writes:
On Tuesday, 1 May 2018 at 12:03:15 UTC, ag0aep6g wrote:
 By the way, you shouldn't mark toString as  trusted when `sink` 
 is  system.
Thanks
May 01 2018
prev sibling parent Q. Schroll <qs.il.paperinik gmail.com> writes:
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).)
May 06 2018