Structural Function Attributes

Q. Schroll via Digitalmars-d digitalmars-d at puremagic.com
Mon Jun 6 13:31:52 PDT 2016


I'm pretty sure, someone before me has thought about that. Take 
pure as an example, but you can replace it by any subset of pure, 
nothrow, @safe and @nogc.

Main reason: Assume a struct with simple opApply
    struct R
    {
       // pure -> error: dg possibly impure
       int opApply(scope int delegate(size_t i, ref int x) dg)
       { ... }
    }
which does only pure operations modulo calls of dg. Then, opApply 
is pure if dg is pure.
Further assume a function using that opApply
    int wannaBePure(R r) // pure -> error: R.opApply possibly 
impure
    {
       int s = 0;
       foreach (i, x; r) s += i*x;
       return s;
    }
PROBLEM 1: Nobody wants to supply all possible combinations of 
attributes the delegate can have. Worse, if the struct R is a 
template, it is nearly impossible for e.g. @nogc.
-> Use a template opApply to infer attributes.
PROBLEM 2: Specific to opApply, you cannot infer the foreach 
types if opApply is a template; even a trivial one or opApply(DG 
: int delegate(size_t, ref int))(scope DG dg) does not do this.
-> Template is an improper solution.
PROBLEM 3: Templates cannot be virtual.
-> Template is not a solution at all.

PROPOSED SOLUTION: For each (strong) attribute like pure, support 
a (weak) structural one, meaning that the function is pure if the 
parameters are.

PLUS: Always infer structural attributes for functions with 
delegate/function parameters, but let the explicit notion be 
possible, as the programmer could want to guarantee structural 
pureness, cf. attribute inference on function templates can be 
denoted explicitly.

FUN FACT: The structural attribute is actually not that weak. It 
makes (maybe many) functions pure, that wouldn't be otherwise.

Back to the example:
    struct R
    {
       struct(pure) // dg pure -> opApply pure
       int opApply(scope int delegate(size_t i, ref int x) dg)
       { ... }
    }
    int wannaBePure(R r) pure // pure opApply -> pure
    {
       int s = 0;
       foreach (i, x; r)
           s += i*x; // pure stuff -> pure delegate -> pure opApply
       return s;
    }

 From what I see, struct(pure) is perfectly sound with inheriting 
and overwriting.
To the overloading set, it can be seen as supplying both versions.
As the code is not affected (only optimizing), this should be 
easy to handle in object code. The function has to be marked as 
sturct(whatever).
The function must be compiled as it wouldn't have the attribute. 
Outside, it can be decided at compile-time.

Ideas?


More information about the Digitalmars-d mailing list