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).)