Bug 5125 – Optional function purity/nothrowness

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P2
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2010-10-27T04:55:00Z
Last change time
2011-06-26T05:03:42Z
Assigned to
nobody
Creator
bearophile_hugs

Comments

Comment #0 by bearophile_hugs — 2010-10-27T04:55:57Z
A higher order function like "map" (or "filter") is meant to be often a pure function, this means that the mapping delegate is often pure (see also bug 3934 and bug 5006 ). A possible solution: the "map" function may be pure if and only if the given delegate/function is pure (a similar problem is present regarding the "nothrow" attribute). But currently there is no good way to do this. This code shows the situation, you need two map functions, and some code duplication: pure int sqr(int x) { return x * x; } pure int[] pureMymap(F)(F f, int[] data) { int[] res; foreach (x; data) res ~= f(x); return res; } int[] mymap(F)(F f, int[] data) { int[] res; foreach (x; data) res ~= f(x); return res; } void main() { int[] data = [1, 2, 3, 4]; auto r1 = pureMymap(&sqr, data); assert(r1 == [1, 4, 9, 16]); int acc = 1; int impure(int x) { acc += x; return acc; } // Error: pure function 'pureMymap' cannot call impure delegate 'f' // auto r2 = pureMymap(&impure, data); auto r2 = mymap(&impure, data); // OK assert(r2 == [2, 4, 7, 11]); } With a string mixing that contains the whole code of map you may remove the code duplication, but it's an ugly solution, and in the end you have two functions with different names still: import std.metastrings: Format; enum string mapCode = q{ %s int[] %smap(F)(F f, int[] data) { int[] res; foreach (x; data) res ~= f(x); return res; } }; mixin(Format!(mapCode, "", "")); // defines map() mixin(Format!(mapCode, "pure", "pure")); // defines pure puremap() pure int sqr(int x) { return x * x; } void main() { int[] data = [1, 2, 3, 4]; auto r1 = puremap(&sqr, data); assert(r1 == [1, 4, 9, 16]); int acc = 1; int impure(int x) { acc += x; return acc; } // Error: pure function 'puremap' cannot call impure delegate 'f' //auto r2 = puremap(&impure, data); auto r2 = map(&impure, data); // OK assert(r2 == [2, 4, 7, 11]); } A better solution is an attribute that allows to use optional tags, like (you may find a better name for this attribute): @optional_tag(isPure!F, pure) int[] map(F)(F f, int[] data) { int[] res; foreach (x; data) res ~= f(x); return res; } The first argument of @optional_tag() is a boolean known at compile-time. Where isPure is just: import std.traits: FunctionAttribute, functionAttributes; template isPure(F) { enum bool isPure = functionAttributes!(F) & FunctionAttribute.PURE; } A more general alternative syntax: __traits(optional, isPure!F, pure) Other more general solutions are possible, this is just a first idea.
Comment #1 by bearophile_hugs — 2010-11-09T04:15:27Z
See bug 4974 for another usage
Comment #2 by bearophile_hugs — 2010-11-14T11:27:06Z
Comment #3 by issues.dlang — 2011-03-04T16:44:23Z
I believe that Andrei's suggestion for pure was to add optional constraints to pure. So, you'd have pure(condition) instead of pure, and the function would be pure if the constraint was true. Presumably, we could do the same for nothrow as well. @safe, @trusted, and @system (or at least @safe and @trusted) are in the same boat. So, maybe it should just be generalized to all function attributes (which would then presumably include stuff like public and private, though that's pretty pointless other than avoiding special casing attributes might be nice). Regardless, we're going to need a solution for this if we want attributes like pure to really work with templated functions.
Comment #4 by bearophile_hugs — 2011-06-26T05:03:42Z
Conditional purity of DMD 2.054 makes this enhancement request unnecessary.