www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Parameterized delegate attributes

reply Eyal Lotem <eyal weka.io> writes:
struct Example {
     int opApply(int delegate(int x) dlg) const {
         return dlg(0);
     }
}

Example.opApply now cannot be used in a  nogc context, even 
though it is fully  nogc whenever dlg is.

Ditto with nothrow, pure,  safe and other attributes.

For a large code-base containing higher-order functions, this 
makes use of these attributes nearly impractical.

What I would like to say is something like:

struct Example {
     int opApply(int delegate(int x) dlg G? nogc T?nothrow P?pure) 
const  G  T  P {
         return dlg(0);
     }
}

The above would assign G to  nogc or nothing, according to 
whether the delegate is.
Ditto with T and nothrow, P and pure.

Then they can be used freely in other parts of the type signature.

Unlike templates, these would not require any generated code 
duplication (they're just for type-checking, after all).

Fixing this along-side the more minor issue of many standard 
libraries lacking proper annotations -- would make  nogc, pure 
and nothrow much more practical.
Sep 28 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 28 September 2016 at 16:20:18 UTC, Eyal Lotem wrote:
 [...]
I would just make opApply a templated function, for the following reasons: 1) You are saying that having this feature in the language would spare some template instantiations and code generations; but a good compiler should be able to recognize functions with the same code and fold them; for example, if a function takes a generic pointer, chances are it doesn't need to be duplicated for every pointer type. 2) It is actually not true that attributes are only for type checking; the compiler can use them to generate different code: a nothrow function does not need machinery to forward exceptions and clean the stack, for example; so templates are the correct way here, because they can generate different code when needed and also be folded to a single codegen if possible.
Sep 28 2016
parent reply pineapple <meapineapple gmail.com> writes:
On Wednesday, 28 September 2016 at 17:00:49 UTC, Lodovico 
Giaretta wrote:
 but a good compiler should be able to recognize functions with 
 the same code and fold them; for example, if a function takes a 
 generic pointer, chances are it doesn't need to be duplicated 
 for every pointer type.
But using a templated opApply currently breaks type inference in `foreach`, right? It'd be really nice if that were fixed.
Sep 28 2016
parent reply Lodovico Giaretta <lodovico giaretart.net> writes:
On Wednesday, 28 September 2016 at 20:23:10 UTC, pineapple wrote:
 But using a templated opApply currently breaks type inference 
 in `foreach`, right?

 It'd be really nice if that were fixed.
Yeah, it would be nice, but I don't think it's technically possible to fix it. The compiler cannot "guess" the correct opApply instantiation if you don't specify the variable type. <SEMI-OT> This is due to opApply "inversion of control", so that instead of your code calling other code, other code calls yours. For this reason, I think that ranges are way more straightforward and should be preferred whenever possible </SEMI-OT> By the way, just to show how opApply can be templated successfully to drive attribute inference: ======================= struct Foo { uint[2] array; int opApply(T : int delegate(ref uint))(scope T dg) { pragma(msg, "Instantiated with " ~ T.stringof); int result = 0; for (int i = 0; i < array.length; i++) { result = dg(array[i]); if (result) break; } return result; } } void main() { Foo a; a.array[0] = 73; a.array[1] = 82; foreach (ref uint u; a) { (() system => u++)(); } foreach (uint u; a) { import std.stdio; writeln(u); } } =======================
Sep 28 2016
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 9/28/16 5:35 PM, Lodovico Giaretta wrote:
 On Wednesday, 28 September 2016 at 20:23:10 UTC, pineapple wrote:
 But using a templated opApply currently breaks type inference in
 `foreach`, right?

 It'd be really nice if that were fixed.
Yeah, it would be nice, but I don't think it's technically possible to fix it. The compiler cannot "guess" the correct opApply instantiation if you don't specify the variable type.
opApply suffers from a few problems. IMO, it should almost always be inlined, so the compiler can "cheat" with attribute inference. It's an interesting idea to apply inferred attributes to a function based on the given delegate, especially for pure and nogc. As I understand it, those attributes don't modify generated code at all, they just restrict what you can call (and in the case of strong pure, allow call-site optimizations). If everything you call aside from the delegate is pure and nogc, then the attributes can be inferred at the call site to be whatever the delegate is, and no separate template generation needs to happen. nothrow, I think, modifies how the code is generated. But it's possible we could do the same here in a way that allows nothrow to be inferred at call site, but generate the stack unwinding code just in case (so only one function is ultimately emitted). Note, we may not need to actually add any syntax for this. If you return auto from opApply, you guarantee the code is available, so the compiler can do the inference. -Steve
Sep 29 2016