digitalmars.D.learn - =?UTF-8?B?Q2Fu4oCZdA==?= use UFCS to create InputRange?
- Ogi (24/25) Apr 29 2020 struct R {}
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (6/16) Apr 29 2020 The template IsInputRange is in the std.range.primitives module,
- user1234 (7/32) Apr 29 2020 The static checker doesn't see your free funcs because to do so
- Simen =?UTF-8?B?S2rDpnLDpXM=?= (37/43) Apr 29 2020 Of course it's possible! :) We can find the context of R (in this
- Paul Backus (9/17) Apr 29 2020 This is basically what C++ calls "argument-dependent lookup."
struct R {} int front(R r) { return 42; } void popFront(R r) {} bool empty(R r) { return false; } void main() { import std.range.primitives : isInputRange; static assert(isInputRange!R); }Error: static assert: `isInputRange!(R)` is falseWhats really weird is that if I replace isInputRange with its definition from std.range.primitives, it returns true: import std; struct R {} int front(R r) { return 42; } void popFront(R r) {} bool empty(R r) { return false; } void main() { static assert(is(typeof(R.init) == R) && is(ReturnType!((R r) => r.empty) == bool) && is(typeof((return ref R r) => r.front)) && !is(ReturnType!((R r) => r.front) == void) && is(typeof((R r) => r.popFront))); } This compiles. What’s going on here?
Apr 29 2020
On Wednesday, 29 April 2020 at 08:34:53 UTC, Ogi wrote:struct R {} int front(R r) { return 42; } void popFront(R r) {} bool empty(R r) { return false; } void main() { import std.range.primitives : isInputRange; static assert(isInputRange!R); }The template IsInputRange is in the std.range.primitives module, and thus can't see the front, popFront and empty definitions in your module. -- SimenError: static assert: `isInputRange!(R)` is falseWhat’s going on here?
Apr 29 2020
On Wednesday, 29 April 2020 at 08:34:53 UTC, Ogi wrote:struct R {} int front(R r) { return 42; } void popFront(R r) {} bool empty(R r) { return false; } void main() { import std.range.primitives : isInputRange; static assert(isInputRange!R); }The static checker doesn't see your free funcs because to do so it would have to import the whole module. (is it possible to do that ? no idea.) Also your signature for the primitives are quite unusual (i.e not idiomatic). Usually they dont take param. Usually we pass a type that contains the member funcs matching to IsIntputRange.Error: static assert: `isInputRange!(R)` is falseWhats really weird is that if I replace isInputRange with its definition from std.range.primitives, it returns true: import std; struct R {} int front(R r) { return 42; } void popFront(R r) {} bool empty(R r) { return false; } void main() { static assert(is(typeof(R.init) == R) && is(ReturnType!((R r) => r.empty) == bool) && is(typeof((return ref R r) => r.front)) && !is(ReturnType!((R r) => r.front) == void) && is(typeof((R r) => r.popFront))); } This compiles. What’s going on here?
Apr 29 2020
On Wednesday, 29 April 2020 at 09:16:58 UTC, user1234 wrote:The static checker doesn't see your free funcs because to do so it would have to import the whole module. (is it possible to do that ? no idea.)Of course it's possible! :) We can find the context of R (in this case, the module) with __traits(parent), and import that: mixin("import "~__traits(parent, R).stringof["module ".length..$]~";"); However, doing that in isInputRange doesn't help much. First, all other range functions would have to do it, and second, just importing into function scope doesn't enable UFCS lookup.Also your signature for the primitives are quite unusual (i.e not idiomatic). Usually they dont take param. Usually we pass a type that contains the member funcs matching to IsIntputRange.You can see a good counterexample to this in https://dlang.org/library/std/range/primitives/pop_front.html, which defines popFront for regular arrays. However, that is the one and only counterexample I know of. Of course, nothing stops us from defining our own front, popFront and friends that combine the two approaches above: template front(R) { auto front(R r) { return __traits(getMember, __traits(parent, R), "front")(r); } } template popFront(R) { auto popFront(R r) { return __traits(getMember, __traits(parent, R), "popFront")(r); } } template empty(R) { auto empty(R r) { return __traits(getMember, __traits(parent, R), "empty")(r); } } We could conceivably add these to std.range.primitives (probably adding some constraints first), and suddenly UFCS ranges are possible! (I am as of yet not convinced that we should, though) -- Simen
Apr 29 2020
On Wednesday, 29 April 2020 at 12:23:11 UTC, Simen Kjærås wrote:Of course, nothing stops us from defining our own front, popFront and friends that combine the two approaches above:[...]We could conceivably add these to std.range.primitives (probably adding some constraints first), and suddenly UFCS ranges are possible! (I am as of yet not convinced that we should, though) -- SimenThis is basically what C++ calls "argument-dependent lookup." Discussion about adding this sort of thing to D has come up before on the forums [1], and iirc Walter has generally been opposed to it. If it were to be added as a library feature, it would probably have to be opt-in. [1] https://forum.dlang.org/post/mailman.123.1472818535.2965.digitalmars-d puremagic.com
Apr 29 2020