On Wednesday, 10 April 2019 at 00:42:11 UTC, Ali Çehreli wrote:
(It may be made to work with Adam's (T : Point!R, R) syntax but I failed just now.)

You know, I didn't think T : Point!T would work, but it does. Huh.

Anyway, the , R one works similarly, observe:

---
T getResponse(T : Point!R, R)(string question) {
    writef("%s (%s): ", question, T.stringof);

    R response;
    readf(" %s", &response);

    return T(response);
}
---

In this case, when you pass it a Point!double, T is still the full type, Point!double (so the return value there needs to be constructed), but R is now just the `double` part, so we can pass that independently to readf.

This pattern is also useful for capturing static arrays:


void processStaticArray(A : T[N], T, size_t N)(ref A) {
        writeln(T.stringof, " ", N);
}

void main()
{
    int[5] arr;
    processStaticArray(arr);
}


There, A is our static array type, but then we deconstruct its pieces to get the size and inner type too. This lets us pass any static array in without losing the compile-time length.

(though usually I'd just slice it and call it T[]!)



These patterns are also used by the `is` expression, and you can rename the whole thing there when used inside `static if`. (I know most people think this is crazy black magic, but it actually isn't that crazy once you know the pattern - write the symbol with placeholders, add a comma, define the placeholders. Then the other forms like is(T == X) and is(T : X) and even is(T N == X) are just optional additions to this - == means is equal to, : means can convert to, and the N in the last one renames the value extracted to something else, most valuable in is(typeof(func) Params == __parameters).

So yeah, there is a bit to it, but it is just a pattern, condition, optional placeholders, optional rename.)

Reply via email to