[Issue 5125] New: Optional function purity/nothrowness

d-bugmail at puremagic.com d-bugmail at puremagic.com
Wed Oct 27 04:57:29 PDT 2010


http://d.puremagic.com/issues/show_bug.cgi?id=5125

           Summary: Optional function purity/nothrowness
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody at puremagic.com
        ReportedBy: bearophile_hugs at eml.cc


--- Comment #0 from bearophile_hugs at eml.cc 2010-10-27 04:55:57 PDT ---
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.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------


More information about the Digitalmars-d-bugs mailing list