digitalmars.D - ADL
- Manu via Digitalmars-d (30/30) Sep 02 2016 In C++, there is this ADL thing (argument dependent lookup).
- David Nadlinger (5/7) Sep 02 2016 Somehow pass in the required information along with the symbol,
- Dicebot (15/25) Sep 02 2016 protected-headers="v1"
- Steven Schveighoffer (15/45) Sep 02 2016 This is a limitation, you can only use struct members given a type, you
- Cauterite (17/17) Sep 02 2016 On Friday, 2 September 2016 at 12:15:25 UTC, Manu wrote:
- Walter Bright (23/26) Sep 02 2016 Yeah, I know about Koening lookup. It was a hack added to C++ to make op...
- Manu via Digitalmars-d (51/78) Sep 02 2016 Naturally you do, and I'm sure that is why it was invented, but you
- Walter Bright (48/73) Sep 02 2016 It's always best to provide an example of the actual problem rather than...
- Stefan Koch (7/9) Sep 02 2016 I could not agree more strongly!
- Walter Bright (2/4) Sep 02 2016 Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.
- Manu via Digitalmars-d (6/12) Sep 02 2016 They're not solutions though, they're workarounds. They're all
- Walter Bright (13/17) Sep 03 2016 What makes them problematic or highly unsavory? I thought #4 in particul...
- Walter Bright (2/3) Sep 03 2016 https://github.com/dlang/phobos/pull/4762
- Manu via Digitalmars-d (4/9) Sep 03 2016 Complexity ramps up further if there are N arguments to the algorithm.
- Timon Gehr (3/14) Sep 03 2016 template adl(string fun){ /* TODO */ }
- Andrei Alexandrescu (2/18) Sep 03 2016 Nice, yah, that kinds of stuff. -- Andrei
- Walter Bright (20/33) Sep 03 2016 I suggest posting the actual problems you're having, because twice now y...
- Walter Bright (11/15) Sep 03 2016 Eh, I realized it's simpler than that. Based on the code I already prese...
- Andrei Alexandrescu (4/23) Sep 03 2016 This only works with the respective modules do define `func`. We need
- Manu via Digitalmars-d (8/38) Sep 03 2016 Right, and it also has to not conflict with possible local
- Walter Bright (8/14) Sep 03 2016 1. 'alias func = ...;' also works to bring in local definitions to an ev...
- vit (23/54) Sep 03 2016 perhaps this:
- Andrei Alexandrescu (3/34) Sep 03 2016 Perhaps too surgical (although nice to have as an option). We need
- Walter Bright (3/6) Sep 03 2016 That's where __traits(compiles, ...) comes in. It can be encapsulated in...
- Andrei Alexandrescu (2/10) Sep 03 2016 Yah. That we need to plant in the standard library. -- Andrei
- Manu via Digitalmars-d (3/23) Sep 03 2016 And if either module doesn't have an instance of func?
- Walter Bright (3/4) Sep 03 2016 static if (traits(compiles, ModuleOf!T.func))
- Andrei Alexandrescu (3/7) Sep 03 2016 What's an elegant way to encapsulate this as a stutter-free one-liner?
- Walter Bright (2/9) Sep 03 2016 using a template and mixing it in
- Andrei Alexandrescu (2/13) Sep 03 2016 "Show me the mo^H^Hcode."
- Walter Bright (9/23) Sep 03 2016 template foo(T)
- Andrei Alexandrescu (2/26) Sep 04 2016 Actually "func" i.e. the function's name must be a paraneter. No? -- And...
- Walter Bright (2/30) Sep 04 2016 Probably. The point is, the tools to do this are available features of D...
- Timon Gehr (2/9) Sep 03 2016 Does not work. Local overloads are not supported.
- Walter Bright (26/27) Sep 03 2016 Yeah, you're right, should have tested that:
- ZombineDev (3/30) Sep 03 2016 What do you think about making overloading and UFCS work with
- Walter Bright (6/8) Sep 04 2016 I'd rather not. Let's make what we have work. There's an unending demand...
- Andrei Alexandrescu (3/15) Sep 04 2016 Might be a sensible enhancement. Removing artificial limitations is good...
- Walter Bright (9/11) Sep 04 2016 The design of executable function bodies is very much "declare before us...
- Timon Gehr (16/29) Sep 04 2016 The rule should be the same as for module-level functions. Note that the...
- Walter Bright (10/17) Sep 04 2016 I know that static if brings with it ordering problems. That's not a
- Manu via Digitalmars-d (8/30) Sep 04 2016 I already worked-around my problems. But the point of my post is that
- Walter Bright (6/12) Sep 04 2016 Try the solutions I proposed - they aren't the ones you have been using....
- Andrei Alexandrescu (29/31) Sep 05 2016 Let me make sure I understand it. The core structure is this:
- Andrei Alexandrescu (2/4) Sep 05 2016 ... "does not apply". -- Andrei
- Jacob Carlborg (5/32) Sep 05 2016 I thought one of the reasons for UFCS was to be able to make a type
- Andrei Alexandrescu (3/5) Sep 05 2016 That is correct (and btw the example should use the member call syntax)....
- Jacob Carlborg (26/28) Sep 05 2016 Not sure what that has to do with anything.
- Andrei Alexandrescu (3/17) Sep 05 2016 Yah, make front a member please. It's in the same module so you're not
- Jacob Carlborg (13/15) Sep 05 2016 I just said:
- Andrei Alexandrescu (7/20) Sep 05 2016 D does not support one module to expand a type defined in another module...
- Lodovico Giaretta (35/36) Sep 05 2016 Yes, but there are a bunch of cases in which the baseline
- Andrei Alexandrescu (11/41) Sep 05 2016 ADL would not apply here because it looks up only names in the same
- Walter Bright (5/10) Sep 05 2016 It would work in C++ because any piece of code can insert more names int...
- Timon Gehr (11/33) Sep 05 2016 I didn't suggest to do that. The sequence can be:
- Marc =?UTF-8?B?U2Now7x0eg==?= (7/26) Sep 05 2016 Can we use a `with` statement? E.g. something along those lines
- Timon Gehr (4/8) Sep 03 2016 Yes they do. It is not possible to implement the range functions as
- Walter Bright (4/11) Sep 03 2016 I don't think it is a template issue. It's a name lookup issue. There's ...
- Timon Gehr (16/31) Sep 03 2016 This is not at all relevant when talking about 'this particular issue'
- Walter Bright (9/22) Sep 03 2016 It was initially justified as a solution for operator overloading, which...
- Jacob Carlborg (19/36) Sep 03 2016 So, something like this:
- Manu via Digitalmars-d (29/37) Sep 03 2016 This is exactly the difference between std.algorithm, and what I was
- Walter Bright (13/42) Sep 03 2016 This is purely a stylistic issue. I, on the other hand, think lambdas ar...
- Tobias M (10/12) Sep 03 2016 I think it is.
- ZombineDev (18/30) Sep 03 2016 No, LINQ doesn't work because of interfaces, but because of
- Tobias M (30/41) Sep 03 2016 I know extension methods, that's not the point.
- ZombineDev (16/57) Sep 03 2016 No you're wrong. There's no need for interfaces or for generic
- Tobias M (13/25) Sep 03 2016 Ok, Interfaces and other generic methods with compatible
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (8/23) Sep 03 2016 It's not runtime reflection, sorry about that.
- ZombineDev (14/40) Sep 03 2016 So what? C#'s generics are less flexible than C++ and D templates.
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (4/29) Sep 03 2016 ADL wouldn't change anything if you don't cast to a specific type, and i...
- ZombineDev (10/25) Sep 03 2016 I agree that it's not a template issue. It's more of a modules vs
- Walter Bright (2/7) Sep 03 2016 Hmm, that explanation could be it. I'm not very familiar with LINQ.
- Andrei Alexandrescu (3/8) Sep 03 2016 Yah, but I don't see this as an issue. The non-members would need to be
- Timon Gehr (5/13) Sep 03 2016 If ADL is done as a fallback, then it is only slower in those cases
- Walter Bright (5/7) Sep 03 2016 That isn't how it works in C++. It's done right up front in finding the
- Walter Bright (2/4) Sep 03 2016 That statement is incorrect. It's used as a fallback in C++. I had forgo...
- Stefan Koch (14/18) Sep 03 2016 True.
- Manu via Digitalmars-d (66/124) Sep 03 2016 This is my current workaround.
- Walter Bright (18/46) Sep 03 2016 // Find module in which T was defined
- John Colvin (25/43) Sep 03 2016 I have had problems with not having C++ style ADL before, but in
- Walter Bright (5/9) Sep 03 2016 Sure it does, usually via a lambda passed to it.
- Walter Bright (17/19) Sep 03 2016 Not at all. No insult was intended. People often find better, more D idi...
- Andrei Alexandrescu (37/46) Sep 03 2016 There's really no need to take offense here. We have a bit of a mimosa
- Manu via Digitalmars-d (68/116) Sep 04 2016 And you did it again.
- Timon Gehr (6/9) Sep 05 2016 I agree with your post except for this.
- Andrei Alexandrescu (2/11) Sep 05 2016 Do you think there should? And if so, what would the algorithm be? -- An...
- deadalnix (24/42) Sep 05 2016 I ran into that very problem many time, and I think it is
- Timon Gehr (14/29) Sep 05 2016 This is a good question. I'm not sure yet what the best solution is.
- Guillaume Boucher (25/34) Sep 06 2016 I don't think cloning a type just to add functionality can
- Manu via Digitalmars-d (3/11) Sep 04 2016 Anyway, I've made my case. I'm watching to see where this goes. I
- Andrei Alexandrescu (20/20) Sep 03 2016 On 9/3/16 3:09 AM, Walter Bright wrote:
- Ethan Watson (16/28) Sep 04 2016 Chipping in to say that I currently do something this with
- Walter Bright (11/13) Sep 04 2016 What about using this template?
- Ethan Watson (19/20) Sep 05 2016 Sure, it'll work assuming the module imports all its symbols
- Tobias =?UTF-8?B?TcO8bGxlcg==?= (9/15) Sep 03 2016 IMO the root of this problem is that templates are *duck typed*.
- Andrei Alexandrescu (2/14) Sep 03 2016 What problems are you referring to? -- Andrei
- Tobias M (6/7) Sep 03 2016 The problems discussed here in the thread related to name lookup
- Andrei Alexandrescu (5/11) Sep 03 2016 I see. This is a matter orthogonal to DbI - introspection should be able...
- Tobias M (5/10) Sep 03 2016 The problem I see with DbI is rather that the user of a function
- Andrei Alexandrescu (2/10) Sep 03 2016 At some point there's a need to RTFM. -- Andrei
- =?UTF-8?Q?Tobias=20M=C3=BCller?= (2/14) Sep 03 2016 Is there one for DbI? (Sincere question)
- Andrei Alexandrescu (5/19) Sep 03 2016 Not yet. We have the allocators body of work, but that's too niche to
- Andrei Alexandrescu (3/5) Sep 03 2016 How do you swap two objects of a generic type that may or may not define...
- Manu via Digitalmars-d (12/18) Sep 03 2016 It's not a problem I've ever had. I'm not actually quite sure I
- Andrei Alexandrescu (9/31) Sep 03 2016 A problem you didn't know you have. It's a classic C++ conundrum
- Walter Bright (8/12) Sep 03 2016 One thing I've noticed in my years of programming and helping others wit...
In C++, there is this ADL thing (argument dependent lookup). What it does is, when searching for overloads, in addition to looking in the local namespace, it also looks in the namespace of the function arguments. D doesn't seem to have this, and that is proving to be quite problematic. What's the work around? C++ example: namespace bob { struct S {}; void f(S s); } namespace joe { struct T {}; void f(T t); void test() { T t; f(t); // obviously works, T is in the local namespace bob::S s; f(s); // local namespace can't see `void f(S)`, but given the argument 's', which is typed bob::S, it will search the bob:: namespace for overloads of f(), so this code compiles successfully. } } I have the same configuration across 2 modules in D. In one module, I receive the foreign modules type via template arg, but I haven't imported that type's module, so when I try to call the function it can't find the overload, because it's not imported, and it doesn't search the argument type's module (ie, namespace) for overloads.
Sep 02 2016
On Friday, 2 September 2016 at 12:15:25 UTC, Manu wrote:D doesn't seem to have this, and that is proving to be quite problematic. What's the work around?Somehow pass in the required information along with the symbol, for example as a member function/alias or an UDA. It's hard to get more specific than that without a concrete example. — David
Sep 02 2016
protected-headers="v1" From: Dicebot <public dicebot.lv> Newsgroups: d,i,g,i,t,a,l,m,a,r,s,.,D Subject: Re: ADL References: <mailman.123.1472818535.2965.digitalmars-d puremagic.com> In-Reply-To: <mailman.123.1472818535.2965.digitalmars-d puremagic.com> --AXCi61NWPxphSaJ0W65PgQxEIkWE8JDpE Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable On 09/02/2016 03:15 PM, Manu via Digitalmars-d wrote:In C++, there is this ADL thing (argument dependent lookup). What it does is, when searching for overloads, in addition to looking in the local namespace, it also looks in the namespace of the function arguments.AFAIR it is intentionally not supported to simplify symbol lookup rules.I have the same configuration across 2 modules in D. In one module, I receive the foreign modules type via template arg, but I haven't imported that type's module, so when I try to call the function it can't find the overload, because it's not imported, and it doesn't search the argument type's module (ie, namespace) for overloads.Don't know about best practice but I tend to simply find the host module for the argument and mixin the import for it inside the template to make symbols directly available. --AXCi61NWPxphSaJ0W65PgQxEIkWE8JDpE--
Sep 02 2016
On 9/2/16 8:15 AM, Manu via Digitalmars-d wrote:In C++, there is this ADL thing (argument dependent lookup). What it does is, when searching for overloads, in addition to looking in the local namespace, it also looks in the namespace of the function arguments. D doesn't seem to have this, and that is proving to be quite problematic. What's the work around? C++ example: namespace bob { struct S {}; void f(S s); } namespace joe { struct T {}; void f(T t); void test() { T t; f(t); // obviously works, T is in the local namespace bob::S s; f(s); // local namespace can't see `void f(S)`, but given the argument 's', which is typed bob::S, it will search the bob:: namespace for overloads of f(), so this code compiles successfully. } } I have the same configuration across 2 modules in D. In one module, I receive the foreign modules type via template arg, but I haven't imported that type's module, so when I try to call the function it can't find the overload, because it's not imported, and it doesn't search the argument type's module (ie, namespace) for overloads.This is a limitation, you can only use struct members given a type, you can't use UFCS functions or regular calls like the above. You need to import the module in the template *definition* file, which is problematic. I think I remember seeing vibe.d do some funky shit to work around this, like getting the fully qualified name, and using string mixins to import the module that defines that type. I think it would be nice to see this fixed, but I'm not sure of the implications. One possibility: import __traits(moduleOf, T); Then you could do this import as a local import when you need it. Of course, this is not as nice as just having the compiler do this automatically. -Steve
Sep 02 2016
On Friday, 2 September 2016 at 12:15:25 UTC, Manu wrote:The only problem I have with this feature is that it would lead to implicit-importation, which is a totally foreign concept in D, and I would assume a design choice to not support it. import bob : S; S s; // implicit `import bob : f;` for this statement: f(s); Is this an acceptable feature? Perhaps; my main concern is that it can be very difficult to work out where this `f` symbol is coming from. If you see the only import statement is `import bob : S;` then you'd naturally assume that the `f` being called here is not `bob.f`. It's nice to learn this term "argument dependent lookup" though. I've spent a lot of time thinking about this feature, but never realised it had a name (and I never realised any languages supported it).
Sep 02 2016
On 9/2/2016 5:15 AM, Manu via Digitalmars-d wrote:In C++, there is this ADL thing (argument dependent lookup).Yeah, I know about Koening lookup. It was a hack added to C++ to make operator overloading work.D doesn't seem to have this,That's right, and it's on purpose :-)and that is proving to be quite problematic. What's the work around?Not a workaround, as D does not need ADL. This is how to do it: extern (C++, bob) { struct S {} void f(S s); } extern (C++, joe) { struct T {} void f(T t); void test() { T t; f(t); // obviously works, T is in the local namespace alias f = bob.f; // bring bob.s into current scope bob.S s; f(s); // no problemo } } The 'alias' construct gives good control over which symbols are visible in which scopes.
Sep 02 2016
On 3 September 2016 at 08:38, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/2/2016 5:15 AM, Manu via Digitalmars-d wrote:Naturally you do, and I'm sure that is why it was invented, but you couldn't write any modern C++ without it. The reason you say it was invented is not the reason that it's useful.In C++, there is this ADL thing (argument dependent lookup).Yeah, I know about Koening lookup. It was a hack added to C++ to make operator overloading work.And that seems to be a rather big problem. The situation is this: In D, it is ***EXTREMELY*** common to have some argument of type T, like, basically everything in D is a template these days... we're talking ranges and stuff. It is also considered un-cool in modern D to aggregate all functionality into types themselves. We want functionality for T to be extensible, so we use UFCS all over the place. Template args combined with UFCS practically demand ADL or something similar to ADL, otherwise you can't really write algorithms. It's impossible to import all the appropriate sources into the file that implements the algorithm. They're unrelataed, except that the algorithm is expected to 'work' on the T it's given. So if someone supplies a T to your algorithm, and it's a range for instance (or something following that pattern), and some part of it's implementation is UFCS, it all falls apart :/ We can't have the situation "UFCS works quite nicely... in this particular subset of common situations".D doesn't seem to have this,That's right, and it's on purpose :-)Now to put it in the terms I describe above, 'test()' is an algorithm, implemented in a module unrelated to bob or joe (I should have given the example with test() outside the namespace)... the algorithm implementation can't go aliasing or importing anything relating to its possible arguments T or S; it's meant to be generic. module bob; struct S {} void f(S s); module joe; struct T {} void f(T t); module myalgorithm; void test(T)(T t) { f(t); } module user_code; import bob, joe; void main() { test(S.init); test(T.init); } This is a better example. I can't be invading test() with any aliases, or imports. It wouldn't be an algorithm anymore if I did that. This pattern seems to bite me every direction I turn when trying to write range or algorithm style code. C++ has ADL, and ADL works. I've never thought about this problem in C++, or had any problems with ADL.and that is proving to be quite problematic. What's the work around?Not a workaround, as D does not need ADL. This is how to do it: extern (C++, bob) { struct S {} void f(S s); } extern (C++, joe) { struct T {} void f(T t); void test() { T t; f(t); // obviously works, T is in the local namespace alias f = bob.f; // bring bob.s into current scope bob.S s; f(s); // no problemo } } The 'alias' construct gives good control over which symbols are visible in which scopes.
Sep 02 2016
On 9/2/2016 4:51 PM, Manu via Digitalmars-d wrote:(I should have given the example with test() outside the namespace)It's always best to provide an example of the actual problem rather than something else.module bob; struct S {} void f(S s); module joe; struct T {} void f(T t); module myalgorithm; void test(T)(T t) { f(t); } module user_code; import bob, joe;import myalgorithm; // neededvoid main() { test(S.init); test(T.init); } This is a better example. I can't be invading test() with any aliases, or imports. It wouldn't be an algorithm anymore if I did that. This pattern seems to bite me every direction I turn when trying to write range or algorithm style code. C++ has ADL, and ADL works. I've never thought about this problem in C++,First solution: module bob; struct S { void f(); } Second solution: module user_code; import bob, joe; import myalgorithm; mixin myalgorithm.test!S; mixin myalgorithm.test!T; void main() { test(S.init); test(T.init); } Third solution: module myalgorithm; void test(M,T)(T t) { M.f(t); } module user_code; import bob, joe; import myalgorithm; void main() { test!bob(S.init); test!joe(T.init); } Fourth solution: module myalgorithm; void test(T)(T t) { import std.traits; mixin("import " ~ std.traits.moduleName!T ~ ";"); mixin("alias M = " ~ std.traits.moduleName!T ~ ";"); // The above could be encapsulated into an eponymous template // that takes T as a parameter and returns the alias M.f(t); }or had any problems with ADLhttps://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D.
Sep 02 2016
On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright wrote:Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D.I could not agree more strongly! If this feature were supported, it would probably break our module system. Even if we could shoehorn it into the language it would make the compiler slower.
Sep 02 2016
On 9/2/2016 6:12 PM, Stefan Koch wrote:If this feature were supported, it would probably break our module system. Even if we could shoehorn it into the language it would make the compiler slower.Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.
Sep 02 2016
On 3 September 2016 at 11:25, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/2/2016 6:12 PM, Stefan Koch wrote:They're not solutions though, they're workarounds. They're all problematic, and highly unsavoury. Nobody is gonna go "oh, i really wish i could do those things in C++!", because the problem is already solved :/If this feature were supported, it would probably break our module system. Even if we could shoehorn it into the language it would make the compiler slower.Note that C++ needs ADL in part because it cannot do options 2, 3 or 4.
Sep 02 2016
On 9/2/2016 9:14 PM, Manu via Digitalmars-d wrote:They're not solutions though, they're workarounds. They're all problematic, and highly unsavoury.rather cool, I plan to use it as an example.Nobody is gonna go "oh, i really wish i could do those things in C++!", because the problem is already solved :/ADL has the problems I provided a link to. In any case, these difficulties are the consequence of trying to write C++ code in D. None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue. Nor have I seen ADL supported in any other language, despite many supporting generic algorithms. I do understand trying to write C++ code in D, because my early FORTRAN programs looked like BASIC, my early C programs looked like FORTRAN, my C++ code looked like C, etc. What I have provided is a generic way to make ADL work in D, which shows how adaptable it is.
Sep 03 2016
On 9/3/2016 1:37 AM, Walter Bright wrote:https://github.com/dlang/phobos/pull/4762
Sep 03 2016
On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/2016 1:37 AM, Walter Bright wrote:Complexity ramps up further if there are N arguments to the algorithm. It needs to search each of the arguments modules.example.https://github.com/dlang/phobos/pull/4762
Sep 03 2016
On 03.09.2016 11:37, Manu via Digitalmars-d wrote:On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:template adl(string fun){ /* TODO */ } adl!"foo"(S.init,T.init);On 9/3/2016 1:37 AM, Walter Bright wrote:Complexity ramps up further if there are N arguments to the algorithm. It needs to search each of the arguments modules.example.https://github.com/dlang/phobos/pull/4762
Sep 03 2016
On 9/3/16 12:03 PM, Timon Gehr wrote:On 03.09.2016 11:37, Manu via Digitalmars-d wrote:Nice, yah, that kinds of stuff. -- AndreiOn 3 September 2016 at 18:56, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:template adl(string fun){ /* TODO */ } adl!"foo"(S.init,T.init);On 9/3/2016 1:37 AM, Walter Bright wrote:Complexity ramps up further if there are N arguments to the algorithm. It needs to search each of the arguments modules.example.https://github.com/dlang/phobos/pull/4762
Sep 03 2016
On 9/3/2016 2:37 AM, Manu via Digitalmars-d wrote:On 3 September 2016 at 18:56, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:I suggest posting the actual problems you're having, because twice now you've gotten solutions to the problems you posted, then said they weren't your actual problems. This template: // Find module in which T was defined template ModuleOf(alias T) { import std.traits : moduleName; mixin("import " ~ moduleName!T ~ ";"); mixin("alias ModuleOf = " ~ moduleName!T ~ ";"); } can be used to qualify any function with the module in which one expects to find it.On 9/3/2016 1:37 AM, Walter Bright wrote:Complexity ramps up further if there are N arguments to the algorithm. It needs to search each of the arguments modules.example.https://github.com/dlang/phobos/pull/4762Complexity ramps up further if there are N arguments to the algorithm. It needs to search each of the arguments modules.Bluntly, if a library is designed around multi-argument ADL as a core requirement, redesign it. I.e. the same advice as for multiple inheritance. It's just not worth it. If you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.
Sep 03 2016
On 9/3/2016 3:12 AM, Walter Bright wrote:If you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 9/3/16 1:24 PM, Walter Bright wrote:On 9/3/2016 3:12 AM, Walter Bright wrote:This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiIf you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 3 September 2016 at 23:04, Andrei Alexandrescu via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/16 1:24 PM, Walter Bright wrote:Right, and it also has to not conflict with possible local definitions, or instances supplied by imports in the local namespace. Ie, the module where T came from is *an additional* place to look, not *the* place to look. I expect that local definitions may exist for things like generic fallbacks, or primitive/builtin type implementations.On 9/3/2016 3:12 AM, Walter Bright wrote:This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiIf you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 9/3/2016 9:01 AM, Manu via Digitalmars-d wrote:Right, and it also has to not conflict with possible local definitions, or instances supplied by imports in the local namespace. Ie, the module where T came from is *an additional* place to look, not *the* place to look. I expect that local definitions may exist for things like generic fallbacks, or primitive/builtin type implementations.1. 'alias func = ...;' also works to bring in local definitions to an even footing with the other alias func statements. 2. Due to recent changes to import lookup rules, imports are searched if locals do not satisfy the lookup. 3. You can use traits(compiles, ...) to not insert the names in the local scope if it already can be looked up. So I believe you're good to go.
Sep 03 2016
On Saturday, 3 September 2016 at 13:04:30 UTC, Andrei Alexandrescu wrote:On 9/3/16 1:24 PM, Walter Bright wrote:perhaps this: auto adl(string fn, T, Args...)(auto ref T x, auto ref Args args){ import std.traits : moduleName, hasMember; import std.meta : Filter, NoDuplicates, staticMap; import std.array : join; static if(hasMember!(T, fn)){ mixin("return x." ~ fn ~ "(args);"); } else{ enum toImportString(T) = "import " ~ moduleName!(T) ~ " : " ~ fn ~ ";"; enum hasModuleFN(T) = __traits(compiles, mixin("(){" ~ toImportString!T ~ "}")); alias Types = Filter!(hasModuleFN, NoDuplicates!(T, Args)); static assert(Types.length, "no property '" ~ fn ~ "' for type '" ~ __traits(identifier, T)~ "'"); mixin([staticMap!(toImportString, Types),"return " ~ fn ~ "(x, args);"].join("\n")); } }On 9/3/2016 3:12 AM, Walter Bright wrote:This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiIf you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 9/3/16 7:00 PM, vit wrote:On Saturday, 3 September 2016 at 13:04:30 UTC, Andrei Alexandrescu wrote:Perhaps too surgical (although nice to have as an option). We need something that pulls the symbol for all purposes. -- AndreiOn 9/3/16 1:24 PM, Walter Bright wrote:perhaps this: auto adl(string fn, T, Args...)(auto ref T x, auto ref Args args)On 9/3/2016 3:12 AM, Walter Bright wrote:This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiIf you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 9/3/2016 6:04 AM, Andrei Alexandrescu wrote:This only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiThat's where __traits(compiles, ...) comes in. It can be encapsulated in another template.
Sep 03 2016
On 9/3/16 10:29 PM, Walter Bright wrote:On 9/3/2016 6:04 AM, Andrei Alexandrescu wrote:Yah. That we need to plant in the standard library. -- AndreiThis only works with the respective modules do define `func`. We need something that conditionally plants the symbol depending on whether the module defines it or not. -- AndreiThat's where __traits(compiles, ...) comes in. It can be encapsulated in another template.
Sep 03 2016
On 3 September 2016 at 21:24, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/2016 3:12 AM, Walter Bright wrote:And if either module doesn't have an instance of func?If you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 03 2016
On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:And if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 03 2016
On 9/3/16 10:43 PM, Walter Bright wrote:On 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 03 2016
On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:On 9/3/16 10:43 PM, Walter Bright wrote:using a template and mixing it inOn 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 03 2016
On 9/4/16 12:28 AM, Walter Bright wrote:On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:"Show me the mo^H^Hcode."On 9/3/16 10:43 PM, Walter Bright wrote:using a template and mixing it inOn 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 03 2016
On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:On 9/4/16 12:28 AM, Walter Bright wrote:template foo(T) { static if (traits(compiles, ModuleOf!T.func)) foo = "alias func = " ~ ModuleOf!T.func ~ ";"; else foo = ""; } mixin(foo!T);On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:"Show me the mo^H^Hcode."On 9/3/16 10:43 PM, Walter Bright wrote:using a template and mixing it inOn 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 03 2016
On 9/4/16 3:26 AM, Walter Bright wrote:On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:Actually "func" i.e. the function's name must be a paraneter. No? -- AndreiOn 9/4/16 12:28 AM, Walter Bright wrote:template foo(T) { static if (traits(compiles, ModuleOf!T.func)) foo = "alias func = " ~ ModuleOf!T.func ~ ";"; else foo = ""; } mixin(foo!T);On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:"Show me the mo^H^Hcode."On 9/3/16 10:43 PM, Walter Bright wrote:using a template and mixing it inOn 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 04 2016
On 9/4/2016 5:31 AM, Andrei Alexandrescu wrote:On 9/4/16 3:26 AM, Walter Bright wrote:Probably. The point is, the tools to do this are available features of D.On 9/3/2016 4:46 PM, Andrei Alexandrescu wrote:Actually "func" i.e. the function's name must be a paraneter. No? -- AndreiOn 9/4/16 12:28 AM, Walter Bright wrote:template foo(T) { static if (traits(compiles, ModuleOf!T.func)) foo = "alias func = " ~ ModuleOf!T.func ~ ";"; else foo = ""; } mixin(foo!T);On 9/3/2016 1:43 PM, Andrei Alexandrescu wrote:"Show me the mo^H^Hcode."On 9/3/16 10:43 PM, Walter Bright wrote:using a template and mixing it inOn 9/3/2016 8:34 AM, Manu via Digitalmars-d wrote:What's an elegant way to encapsulate this as a stutter-free one-liner? -- AndreiAnd if either module doesn't have an instance of func?static if (traits(compiles, ModuleOf!T.func)) alias func = ModuleOf!T.func;
Sep 04 2016
On 03.09.2016 13:24, Walter Bright wrote:Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }Does not work. Local overloads are not supported.
Sep 03 2016
On 9/3/2016 5:36 PM, Timon Gehr wrote:Does not work. Local overloads are not supported.Yeah, you're right, should have tested that: void abc(int); void def(uint); void foo() { alias func = abc; alias func = def; // error func(1); } fails. Pushing it out a level works: void abc(int); void def(uint); template foo() { alias func = abc; alias func = def; void foo() { func(1); } } void main() { foo(); }
Sep 03 2016
On Sunday, 4 September 2016 at 01:34:47 UTC, Walter Bright wrote:On 9/3/2016 5:36 PM, Timon Gehr wrote:What do you think about making overloading and UFCS work with local symbols? There are workarounds, but nothing pretty.Does not work. Local overloads are not supported.Yeah, you're right, should have tested that: void abc(int); void def(uint); void foo() { alias func = abc; alias func = def; // error func(1); } fails. Pushing it out a level works: void abc(int); void def(uint); template foo() { alias func = abc; alias func = def; void foo() { func(1); } } void main() { foo(); }
Sep 03 2016
On 9/3/2016 10:57 PM, ZombineDev wrote:What do you think about making overloading and UFCS work with local symbols?I'd rather not. Let's make what we have work. There's an unending demand for new features in the core language.There are workarounds, but nothing pretty.I don't regard this as a workaround. It's how things were designed to work in D. The fact that it isn't the minimum number of characters doesn't make it a workaround.
Sep 04 2016
On 9/4/16 2:36 AM, Timon Gehr wrote:On 03.09.2016 13:24, Walter Bright wrote:Might be a sensible enhancement. Removing artificial limitations is good programming language design. Turtles! -- AndreiSomething like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }Does not work. Local overloads are not supported.
Sep 04 2016
On 9/4/2016 5:30 AM, Andrei Alexandrescu wrote:Might be a sensible enhancement. Removing artificial limitations is good programming language design. Turtles! -- AndreiThe design of executable function bodies is very much "declare before use", quite unlike at the declaration levels which is all "order is not relevant". Changing this will have consequences (such as our discussion of exactly when a declaration becomes valid), and I just feel that function code is just easier to understand if "declare before use" is the rule, because that is how we reason about how it is executed. Besides, I showed a method of how the overloads could be done with the existing language.
Sep 04 2016
On 04.09.2016 22:22, Walter Bright wrote:On 9/4/2016 5:30 AM, Andrei Alexandrescu wrote:The rule should be the same as for module-level functions. Note that the rules for module-level functions are currently inadequate: pragma(msg, foo(0)); // calls double overload static if(foo(0)){ // calls double overload bool foo(int x){ return false; } } bool foo(double x){ return true; } pragma(msg, foo(0)); // calls int overload Declare-call ordering issues for overload sets are not limited to local scopes. This problem needs to be solved anyway. The fact that the scope is local adds exactly zero additional complications.Might be a sensible enhancement. Removing artificial limitations is good programming language design. Turtles! -- AndreiThe design of executable function bodies is very much "declare before use", quite unlike at the declaration levels which is all "order is not relevant". Changing this will have consequences (such as our discussion of exactly when a declaration becomes valid),and I just feel that function code is just easier to understand if "declare before use" is the rule,Overloading does not violate declare before use. You seem to be conflating it with forward referencing.because that is how we reason about how it is executed. Besides, I showed a method of how the overloads could be done with the existing language.That's not the point. What's perhaps more telling is that you initially got it wrong. It /wants/ to be valid code.
Sep 04 2016
On 9/4/2016 2:36 PM, Timon Gehr wrote:Declare-call ordering issues for overload sets are not limited to local scopes. This problem needs to be solved anyway. The fact that the scope is local adds exactly zero additional complications.I know that static if brings with it ordering problems. That's not a justification for adding them to statements.Maybe, but if I redesigned the language for every mistake I made, nothing would get done. My point with all this is ADL-workalike behavior can be reasonably done with existing D core features available *now* in all 3 compilers. It means we don't have to panic and rewrite the compiler right now - Manu can use these techniques and get his work done, even though it isn't quite what he envisions. He's not dead in the water.Besides, I showed a method of how the overloads could be done with the existing language.That's not the point. What's perhaps more telling is that you initially got it wrong. It /wants/ to be valid code.
Sep 04 2016
On 5 September 2016 at 10:50, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/4/2016 2:36 PM, Timon Gehr wrote:I already worked-around my problems. But the point of my post is that I feel the problem is of very high importance. I don't think the situation is okay, since we're making design recommendations that lead straight to these problems. And these modern D design patterns are the thing in D I'm most excited about, and keen to share with not-yet-D-users.Declare-call ordering issues for overload sets are not limited to local scopes. This problem needs to be solved anyway. The fact that the scope is local adds exactly zero additional complications.I know that static if brings with it ordering problems. That's not a justification for adding them to statements.Maybe, but if I redesigned the language for every mistake I made, nothing would get done. My point with all this is ADL-workalike behavior can be reasonably done with existing D core features available *now* in all 3 compilers. It means we don't have to panic and rewrite the compiler right now - Manu can use these techniques and get his work done, even though it isn't quite what he envisions. He's not dead in the water.Besides, I showed a method of how the overloads could be done with the existing language.That's not the point. What's perhaps more telling is that you initially got it wrong. It /wants/ to be valid code.
Sep 04 2016
On 9/4/2016 9:23 PM, Manu via Digitalmars-d wrote:I already worked-around my problems. But the point of my post is that I feel the problem is of very high importance. I don't think the situation is okay, since we're making design recommendations that lead straight to these problems. And these modern D design patterns are the thing in D I'm most excited about, and keen to share with not-yet-D-users.Try the solutions I proposed - they aren't the ones you have been using. Give 'em a chance! As pointed out, C++ ADL is an awkward feature with ugly corner cases. If we add it to D, we'll be forevermore stuck with that. The library solutions presented here do work, and don't suffer from those problems.
Sep 04 2016
On 9/5/16 6:23 AM, Manu via Digitalmars-d wrote:But the point of my post is that I feel the problem is of very high importance.Let me make sure I understand it. The core structure is this: ===== module bob; struct S {} void f(S s); module myalgorithm; void test(T)(T t) { f(t); } ===== The core issue here is that f is not considered for lookup. It is a free function in the same module as S. That's not a frequent case and it seems right to not support it in the lookup rules. The simplest solution, which has already been discussed, is to make f a member of S. It is important that does not affect modularity; all protection in D has module-level granularity, so the premise of http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197. So it's for the most part a clerical matter: move the body of f inside S, or if f is already generic define an alias for it inside of S. This is the baseline solution, and it is reasonable. This needs to be properly understood before we look into any others: by a simple mechanical intervention, everything works properly. Every language has such minute needs for minor scaffolding. It must also be understood that changing the lookup rules to make this scaffolding unnecessary bring with them a host of unpleasant consequences. Are we in agreement about the baseline solution? Andrei
Sep 05 2016
On 9/5/16 10:17 AM, Andrei Alexandrescu wrote:so the premise of http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197... "does not apply". -- Andrei
Sep 05 2016
On 2016-09-05 10:17, Andrei Alexandrescu wrote:Let me make sure I understand it. The core structure is this: ===== module bob; struct S {} void f(S s); module myalgorithm; void test(T)(T t) { f(t); } ===== The core issue here is that f is not considered for lookup. It is a free function in the same module as S. That's not a frequent case and it seems right to not support it in the lookup rules. The simplest solution, which has already been discussed, is to make f a member of S. It is important that does not affect modularity; all protection in D has module-level granularity, so the premise of http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197. So it's for the most part a clerical matter: move the body of f inside S, or if f is already generic define an alias for it inside of S. This is the baseline solution, and it is reasonable. This needs to be properly understood before we look into any others: by a simple mechanical intervention, everything works properly. Every language has such minute needs for minor scaffolding. It must also be understood that changing the lookup rules to make this scaffolding unnecessary bring with them a host of unpleasant consequences. Are we in agreement about the baseline solution?I thought one of the reasons for UFCS was to be able to make a type support the range interface without modifying the type. -- /Jacob Carlborg
Sep 05 2016
On 9/5/16 10:55 AM, Jacob Carlborg wrote:I thought one of the reasons for UFCS was to be able to make a type support the range interface without modifying the type.That is correct (and btw the example should use the member call syntax). But touching a type's module is modifying the type. -- Andrei
Sep 05 2016
On 2016-09-05 11:06, Andrei Alexandrescu wrote:That is correct (and btw the example should use the member call syntax). But touching a type's module is modifying the type. -- AndreiNot sure what that has to do with anything. Example: module foo; struct Foo { int[] array = [1]; } int front(Foo foo) { return foo.array[0]; } module algo; void algorithm(Range)(Range range) { auto e = range.front; // Error: no property 'front' for type 'Foo' } module main import foo; import algo; void main() { algorithm(Foo()); } -- /Jacob Carlborg
Sep 05 2016
On 9/5/16 11:25 AM, Jacob Carlborg wrote:On 2016-09-05 11:06, Andrei Alexandrescu wrote:Yah, make front a member please. It's in the same module so you're not breaking any encapsulation anyway. -- AndreiThat is correct (and btw the example should use the member call syntax). But touching a type's module is modifying the type. -- AndreiNot sure what that has to do with anything. Example: module foo; struct Foo { int[] array = [1]; } int front(Foo foo) { return foo.array[0]; }
Sep 05 2016
On 2016-09-05 15:28, Andrei Alexandrescu wrote:Yah, make front a member please. It's in the same module so you're not breaking any encapsulation anyway. -- AndreiI just said: "I thought one of the reasons for UFCS was to be able to make a type support the range interface without modifying the type" [1]. And you replied: "That is correct" [2]. And now you're saying that it should be a member? What if it's in the different module? Or as it is for the built-in arrays, not possible to add a member there. [1] http://forum.dlang.org/post/nqjbu4$1i7h$1 digitalmars.com [2] http://forum.dlang.org/post/nqjcj6$1j3b$1 digitalmars.com -- /Jacob Carlborg
Sep 05 2016
On 9/5/16 4:41 PM, Jacob Carlborg wrote:On 2016-09-05 15:28, Andrei Alexandrescu wrote:That's the path of last resistance.Yah, make front a member please. It's in the same module so you're not breaking any encapsulation anyway. -- AndreiI just said: "I thought one of the reasons for UFCS was to be able to make a type support the range interface without modifying the type" [1]. And you replied: "That is correct" [2]. And now you're saying that it should be a member?What if it's in the different module?D does not support one module to expand a type defined in another module with 100% transparency.Or as it is for the built-in arrays, not possible to add a member there.That pattern is only possible with the restrictions and limitations of std.array. Andrei
Sep 05 2016
On Monday, 5 September 2016 at 08:17:15 UTC, Andrei Alexandrescu wrote:Are we in agreement about the baseline solution?Yes, but there are a bunch of cases in which the baseline solution is not applicable. Disclaimer: I don't know how C++ would handle the following situation. Let's say I use a library that exposes a type T. Of course the library does not expose a range interface for it. So I create a module t_range, which provides free range functions for type T. This is akin to what Phobos does for arrays, whose range functions are in std.array. Now I want to use std.algorithm on T, as I would use it on arrays. But I can't, because the only reason std.algorithm works on arrays is because it imports std.array. But of course it cannot import my module t_range. What I'd like as a solution is that the template does not only look in its module, but also in the instantiating module, to resolve symbols dependent on the template types. So std.algorithm should not import std.array, but should work on arrays if the instantiating module imports std.array, and should work on T if the instantiating module imports t_range, which is a sound behaviour that would not surprise anyone. Incidentally, this would also solve the problem of emplacing objects with private or package constructors and probably also some of the current discussions about visibility of private members in templates (private members would be visible only if they are visible at the instantiation site) So templates would work as if they were mixed-in the instantiating module, and they would work as if the user explicitly wrote that piece of code specialized for its case directly in the usage site (which is what templates are for: avoiding to write every possible specialization). I know this is a big breaking change that will probably never happen, but I think it would be an interesting scenario and would open lots of possibilities. What do you think?
Sep 05 2016
On 9/5/16 11:39 AM, Lodovico Giaretta wrote:On Monday, 5 September 2016 at 08:17:15 UTC, Andrei Alexandrescu wrote:ADL would not apply here because it looks up only names in the same module as the type.Are we in agreement about the baseline solution?Yes, but there are a bunch of cases in which the baseline solution is not applicable. Disclaimer: I don't know how C++ would handle the following situation. Let's say I use a library that exposes a type T. Of course the library does not expose a range interface for it. So I create a module t_range, which provides free range functions for type T. This is akin to what Phobos does for arrays, whose range functions are in std.array.Now I want to use std.algorithm on T, as I would use it on arrays. But I can't, because the only reason std.algorithm works on arrays is because it imports std.array. But of course it cannot import my module t_range. What I'd like as a solution is that the template does not only look in its module, but also in the instantiating module, to resolve symbols dependent on the template types. So std.algorithm should not import std.array, but should work on arrays if the instantiating module imports std.array, and should work on T if the instantiating module imports t_range, which is a sound behaviour that would not surprise anyone. Incidentally, this would also solve the problem of emplacing objects with private or package constructors and probably also some of the current discussions about visibility of private members in templates (private members would be visible only if they are visible at the instantiation site) So templates would work as if they were mixed-in the instantiating module, and they would work as if the user explicitly wrote that piece of code specialized for its case directly in the usage site (which is what templates are for: avoiding to write every possible specialization). I know this is a big breaking change that will probably never happen, but I think it would be an interesting scenario and would open lots of possibilities. What do you think?It doesn't sound like a good idea. This kind of lookup that collects names from various places and holds an auction is fraught with Byzantine failures. Good lookup is regular and predictable. To implement the range interface for a type, provide a wrapper. KISS. D is powerful and that has the danger of making people entitled to ask for clever solutions to any problem there is, but at some point it just comes down to write some code pulp. Andrei
Sep 05 2016
On 9/5/2016 6:34 AM, Andrei Alexandrescu wrote:ADL would not apply here because it looks up only names in the same module as the type.It would work in C++ because any piece of code can insert more names into any namespace. Inserting names into a namespace violates about every principle of encapsulation I can think of, and it just ain't worth it.It doesn't sound like a good idea. This kind of lookup that collects names from various places and holds an auction is fraught with Byzantine failures. Good lookup is regular and predictable.I agree.
Sep 05 2016
On 05.09.2016 02:50, Walter Bright wrote:On 9/4/2016 2:36 PM, Timon Gehr wrote:I didn't suggest to do that. The sequence can be: 1. Fix ordering problems for overload sets generically. 2. Allow local overloads. or 1. Allow local overloads with some additional restrictions ensuring no ordering problems. 2. Fix ordering problem for overload sets generically. 3. Remove additional restrictions for local overloads.Declare-call ordering issues for overload sets are not limited to local scopes. This problem needs to be solved anyway. The fact that the scope is local adds exactly zero additional complications.I know that static if brings with it ordering problems. That's not a justification for adding them to statements. ...The mistake is arguably in the language design here. (Lack of turtles.)Maybe, but if I redesigned the language for every mistake I made, nothing would get done. ...Besides, I showed a method of how the overloads could be done with the existing language.That's not the point. What's perhaps more telling is that you initially got it wrong. It /wants/ to be valid code.My point with all this is ADL-workalike behavior can be reasonably done with existing D core features available *now* in all 3 compilers. It means we don't have to panic and rewrite the compiler right now - Manu can use these techniques and get his work done, even though it isn't quite what he envisions. He's not dead in the water.Yup.
Sep 05 2016
On Saturday, 3 September 2016 at 11:24:01 UTC, Walter Bright wrote:On 9/3/2016 3:12 AM, Walter Bright wrote:Can we use a `with` statement? E.g. something along those lines void foo(T, U, alias context = __CURRENT_MODULE__)(T t, U u) { with(context) return func(t, u); }If you are still determined to use it, you can use: __traits(compiles, ...) like you would SFINAE in C++ to select which of the modules from the argument types selects a function that compiles.Eh, I realized it's simpler than that. Based on the code I already presented, each argument can be used to generate an import for its corresponding version of the function. Then, overloading rules apply and it works. Something like: Something like: void foo(T,U)(T t, U u) { alias func = ModuleOf!T.func; alias func = ModuleOf!U.func; func(t, u); }
Sep 05 2016
On 03.09.2016 10:37, Walter Bright wrote:None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue.Yes they do. It is not possible to implement the range functions as non-members.Nor have I seen ADL supported in any other language, despite many supporting generic algorithms.Which other such languages have templates like D or C++?
Sep 03 2016
On 9/3/2016 3:14 AM, Timon Gehr wrote:On 03.09.2016 10:37, Walter Bright wrote:It's done for arrays via std.array.None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue.Yes they do. It is not possible to implement the range functions as non-members.I don't think it is a template issue. It's a name lookup issue. There's LINQ inNor have I seen ADL supported in any other language, despite many supporting generic algorithms.Which other such languages have templates like D or C++?
Sep 03 2016
On 03.09.2016 12:33, Walter Bright wrote:On 9/3/2016 3:14 AM, Timon Gehr wrote:This is not at all relevant when talking about 'this particular issue' that Manu brought up. std.range and std.algorithm import std.array.On 03.09.2016 10:37, Walter Bright wrote:It's done for arrays via std.array. ...None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue.Yes they do. It is not possible to implement the range functions as non-members.It's both. ADL is mostly useless outside of generic code. It's mostly about how templates specify what interface they require and how the requirements are satisfied by the caller. ADL is a workaround for the lack of a convenient enough such protocol in templates. Other approaches to generics solve this particular issue quite elegantly. (E.g. type classes implicitly pass along the required free-function functionality.) D's templates don't, this is why it is a template issue. By default, name lookup does not work in a way that would allow you to actually extend types using UFCS, and therefore none of Phobos works that way. Note that I'm not saying ADL should be implemented, but the problem it addresses is real, and it exists in D.I don't think it is a template issue. It's a name lookup issue.Nor have I seen ADL supported in any other language, despite many supporting generic algorithms.Which other such languages have templates like D or C++?
Sep 03 2016
On 9/3/2016 3:51 AM, Timon Gehr wrote:It was initially justified as a solution for operator overloading, which has no necessary relationship to templates or generic programming.I don't think it is a template issue. It's a name lookup issue.It's both. ADL is mostly useless outside of generic code.It's mostly about how templates specify what interface they require and how the requirements are satisfied by the caller. ADL is a workaround for the lack of a convenient enough such protocol in templates. Other approaches to generics solve this particular issue quite elegantly. (E.g. type classes implicitly pass along the required free-function functionality.) D's templates don't, this is why it is a template issue. By default, name lookup does not work in a way that would allow you to actually extend types using UFCS, and therefore none of Phobos works that way.Lambdas! (Besides, I showed how other scopes can be imported based on a type, and then things can be looked up in those scopes, and UFCS applied.)It has generics: https://msdn.microsoft.com/en-us/library/512aeb7t.aspx and iterators and algorithms and LINQ.
Sep 03 2016
On 2016-09-03 13:16, Walter Bright wrote:So, something like this: module foo; struct Foo {} int front(Foo f); void popFront(Foo f); bool empty(Foo f); module algo; void algorithm(alias front, alias popFront, alias empty, T)(T t); module user; import foo; import algo; void main() { Foo f; algorithm!(() => f.front, () => f.popFront(), () => f.empty)(f); } -- /Jacob CarlborgIt's mostly about how templates specify what interface they require and how the requirements are satisfied by the caller. ADL is a workaround for the lack of a convenient enough such protocol in templates. Other approaches to generics solve this particular issue quite elegantly. (E.g. type classes implicitly pass along the required free-function functionality.) D's templates don't, this is why it is a template issue. By default, name lookup does not work in a way that would allow you to actually extend types using UFCS, and therefore none of Phobos works that way.Lambdas!
Sep 03 2016
On 3 September 2016 at 21:16, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/2016 3:51 AM, Timon Gehr wrote:This is exactly the difference between std.algorithm, and what I was trying to express as an algorithm that 'does work'. It's not the business of the API for the user to supply the work to do (ie, via lambda); the function is meant to do the work, which means it needs to call other functions. There are no lambdas to be seen in this situation.By default, name lookup does not work in a way that would allow you to actually extend types using UFCS, and therefore none of Phobos works that way.Lambdas!(Besides, I showed how other scopes can be imported based on a type, and then things can be looked up in those scopes, and UFCS applied.)I still think that's unnecessarily complicated, and multiple arguments leads to static if-ing and __traits(compiles,...). The fact the user needs to intervene at all is already too much. Apparently I need to stress again, this is a *core value proposition of D*... It's presented as "this is modern D code", and yet it's awkward and requires careful handling or you get hard to understand name-resolution issues. UFCS *is* modern D. Algorithms and ranges *is* modern D. Seriously, this is the style that modern D aspires to, and it doesn't 'just work'. There should be ear-piercing alarms and flashing red everywhere. This comes up for me frequently, yet ADL has never caused me a single moments trouble, in 15+ years. I didn't even know ADL existed until I started running into this problem in D and then wondered to myself why I never encountered the same issue in C++. It worked so seamlessly and intuitively, I didn't even know it was there. I don't care if the solution is ADL like C++, or something else that works, just that this problem is real; it's a massive fly in the ointment of modern D style, and I don't think it's acceptable. It needs a seamless solution, not manual intervention at every case. D depends on this so much more than C++ does.
Sep 03 2016
On 9/3/2016 8:51 AM, Manu via Digitalmars-d wrote:This is exactly the difference between std.algorithm, and what I was trying to express as an algorithm that 'does work'. It's not the business of the API for the user to supply the work to do (ie, via lambda); the function is meant to do the work, which means it needs to call other functions. There are no lambdas to be seen in this situation.This is purely a stylistic issue. I, on the other hand, think lambdas are a superior design, as ADL lookups never sat well with me because it can be quite difficult for the user to figure out where 'func' is coming from.I already showed how your previous objection to this could be folded away inside a nice template.(Besides, I showed how other scopes can be imported based on a type, and then things can be looked up in those scopes, and UFCS applied.)I still think that's unnecessarily complicated, and multiple arguments leads to static if-ing and __traits(compiles,...). The fact the user needs to intervene at all is already too much.Apparently I need to stress again, this is a *core value proposition of D*... It's presented as "this is modern D code", and yet it's awkward and requires careful handling or you get hard to understand name-resolution issues.As mentioned, I don't see anything hard to understand about it - it makes it clear to the reader where names are coming from. ADL pulls names in from who knows where.UFCS *is* modern D. Algorithms and ranges *is* modern D. Seriously, this is the style that modern D aspires to, and it doesn't 'just work'. There should be ear-piercing alarms and flashing red everywhere. This comes up for me frequently, yet ADL has never caused me a single moments trouble, in 15+ years. I didn't even know ADL existed until I started running into this problem in D and then wondered to myself why I never encountered the same issue in C++. It worked so seamlessly and intuitively, I didn't even know it was there. I don't care if the solution is ADL like C++, or something else that works, just that this problem is real; it's a massive fly in the ointment of modern D style, and I don't think it's acceptable. It needs a seamless solution, not manual intervention at every case. D depends on this so much more than C++ does.You've used ADL for 15 years. You're obviously very comfortable with it. I suggest trying out using lambdas enough to feel comfortable with it before deciding that ADL is better. New coding styles are rarely comfortable right off the bat.
Sep 03 2016
On Saturday, 3 September 2016 at 10:33:22 UTC, Walter Bright wrote:I don't think it is a template issue. It's a name lookup issue.I think it is. The problem is lookup of dependent symbols (see C++ two phase lookup). Without real templates, all lookup can be done at definition time. interfaces as constraints on generics, similar to traits/type classes. Lookup is then done once, considering only the interfaces, not for each the concrete type.
Sep 03 2016
On Saturday, 3 September 2016 at 10:56:20 UTC, Tobias M wrote:On Saturday, 3 September 2016 at 10:33:22 UTC, Walter Bright wrote:No, LINQ doesn't work because of interfaces, but because of interface defines only a single method. All the useful functionality is implemented as extension methods which are only available if the user specifically imports the namespace in which where they're defined (just like D's ranges and range primitive implementations for arrays). Those extension methods are used as a fallback, similarly to UFCS in D: every type can override the extension methods by implementing the method itself. Also more inner namespaces (more closer to the method invocation) override more outer namespaces. For more info see: 1) https://github.com/ljw1004/csharpspec/blob/gh-pages/expressi ns.md#member-lookup Member Lookup 2) https://github.com/ljw1004/csharpspec/blob/gh-pages/expressi ns.md#member-access Member access and 3) https://github.com/ljw1004/csharpspec/blob/gh-pages/expressions.md#extensio -method-invocations Extension method invocationsI don't think it is a template issue. It's a name lookupI think it is. The problem is lookup of dependent symbols (see C++ two phase lookup). Without real templates, all lookup can be done at definition time. interfaces as constraints on generics, similar to traits/type classes. Lookup is then done once, considering only the interfaces, not for each the concrete type.
Sep 03 2016
On Saturday, 3 September 2016 at 12:40:26 UTC, ZombineDev wrote:No, LINQ doesn't work because of interfaces, but because of interface defines only a single method. All the useful functionality is implemented as extension methods which are only available if the user specifically imports the namespace in which where they're defined (just like D's ranges and range primitive implementations for arrays). Those extension methods are used as a fallback, similarly to UFCS in D: every type can override the extension methods by implementing the method itself. Also more inner namespaces (more closer to the method invocation) override more outer namespaces.I know extension methods, that's not the point. The point is, that you cannot have a generic method like this in class Bar { void GenericMethod<T>(T arg) { arg.Foo(); } } Instead you need a constraint like this: interface IFoo { void Foo(); } class Bar { void GenericMethod<T>(T arg) where T: IFoo { arg.Foo(); } } Similarly for LINQ, you cannot just implement a generic "Sum" extension method for IEnumerable<T> that works for all T, because you cannot just use the + operator in that method. It is not defined on T if there are no respective constraints. Look at how it is implemented separately for every type T that supports +: https://msdn.microsoft.com/de-de/library/system.linq.enumerable.sum(v=vs.110).aspx
Sep 03 2016
On Saturday, 3 September 2016 at 14:05:11 UTC, Tobias M wrote:On Saturday, 3 September 2016 at 12:40:26 UTC, ZombineDev wrote:No you're wrong. There's no need for interfaces or for generic constraints. It's not static vs duck typing. It's just a method lookup issue. See for yourself: http://rextester.com/GFKNSK99121No, LINQ doesn't work because of interfaces, but because of interface defines only a single method. All the useful functionality is implemented as extension methods which are only available if the user specifically imports the namespace in which where they're defined (just like D's ranges and range primitive implementations for arrays). Those extension methods are used as a fallback, similarly to UFCS in D: every type can override the extension methods by implementing the method itself. Also more inner namespaces (more closer to the method invocation) override more outer namespaces.I know extension methods, that's not the point. The point is, that you cannot have a generic method like this class Bar { void GenericMethod<T>(T arg) { arg.Foo(); } } Instead you need a constraint like this: interface IFoo { void Foo(); } class Bar { void GenericMethod<T>(T arg) where T: IFoo { arg.Foo(); } }Similarly for LINQ, you cannot just implement a generic "Sum" extension method for IEnumerable<T> that works for all T, because you cannot just use the + operator in that method. It is not defined on T if there are no respective constraints. Look at how it is implemented separately for every type T that supports +: https://msdn.microsoft.com/de-de/library/system.linq.enumerable.sum(v=vs.110).aspxoperators need to be implemented as static methods, so you can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087 The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example. I'm guessing that operator overloading was designed that way because: 1) they're worried about boxing and virtual call overhead 2) operator overloading was designed before generics (IIRC).
Sep 03 2016
On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:No you're wrong. There's no need for interfaces or for generic constraints. It's not static vs duck typing. It's just a method lookup issue. See for yourself: http://rextester.com/GFKNSK99121Ok, Interfaces and other generic methods with compatible constraints. But in the end you cannot do much without any interface constraints except writing out to the console as you do in the example. But the main point still holds, name lookup is only done at definition time, not at instantiation time. That's why you can only call generic methods. Overloads don't work.Sum is implemented in that stupid way, because unlike C++, in can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087 The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example.With runtime reflection you can do almost anything... That's circumventing the type system and doesn't disprove anything. I mean, it even "works" for types that cannot be added at all, by just returning a default value...
Sep 03 2016
Tobias M <troplin bluewin.ch> wrote:On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:It's not runtime reflection, sorry about that. But Add claims to be generic but it's actually just a list of special cases. It compiles for all types but only works for some. And even worse, for types that actually do support addition but are not in the list it silently does the wrong thing. You cannot do the same in a truly generic way.Sum is implemented in that stupid way, because unlike C++, in can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087 The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example.With runtime reflection you can do almost anything... That's circumventing the type system and doesn't disprove anything. I mean, it even "works" for types that cannot be added at all, by just returning a default value...
Sep 03 2016
On Saturday, 3 September 2016 at 17:05:35 UTC, Tobias M wrote:On Saturday, 3 September 2016 at 16:32:16 UTC, ZombineDev wrote:implemented interfaces, but also falls back to extensions the compiler would also look for extension methods in the namespace of the type (in non-generic methods, when the type is "known"), although the user of the type may not have imported the namespace.No you're wrong. There's no need for interfaces or for generic constraints. It's not static vs duck typing. It's just a method lookup issue. See for yourself: http://rextester.com/GFKNSK99121Ok, Interfaces and other generic methods with compatible constraints. But in the end you cannot do much without any interface constraints except writing out to the console as you do in the example. But the main point still holds, name lookup is only done at definition time, not at instantiation time. That's why you can only call generic methods. Overloads don't work.There's no circumventing the type system. `typeof(obj)` is barely even reflection. You can do this with regular cast or using the `is` expression (http://rextester.com/CXGNK69048). I used `typeof` just because it could yield better performance.Sum is implemented in that stupid way, because unlike C++, in can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087 The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example.With runtime reflection you can do almost anything... That's circumventing the type system and doesn't disprove anything.I mean, it even "works" for types that cannot be added at all, by just returning a default value...? Sorry, I don't understand, what's the problem?
Sep 03 2016
ZombineDev <petar.p.kirov gmail.com> wrote:implemented interfaces, but also falls back to extensions the compiler would also look for extension methods in the namespace of the type (in non-generic methods, when the type is "known"), although the user of the type may not have imported the namespace.ADL wouldn't change anything if you don't cast to a specific type, and if you do, that part of the code is not generic anymore.Typecasting *is* circumventing the type system.There's no circumventing the type system. `typeof(obj)` is barely even reflection. You can do this with regular cast or using the `is` expression (http://rextester.com/CXGNK69048). I used `typeof` just because it could yield better performance.Sum is implemented in that stupid way, because unlike C++, in can't abstract them with an interface. If they were instance methods, you could implement them outside of the class as extension methods and there would be no need to write a distinct method for each type. Here's an example: http://rextester.com/PQFPC46087 The only thing missing is syntax sugar to forward the '+' operator to 'Add' in my example.With runtime reflection you can do almost anything... That's circumventing the type system and doesn't disprove anything.
Sep 03 2016
On Saturday, 3 September 2016 at 10:33:22 UTC, Walter Bright wrote:On 9/3/2016 3:14 AM, Timon Gehr wrote:I agree that it's not a template issue. It's more of a modules vs because everyone can (and everyone does) extend an existing namespace, so most user's of LINQ algorithms just slap a `using System.Linq` on top of the file without caring in exactly which file the functionality they're using is coming from. Personally I'm glad that D doesn't have ADL because ADL can make it very hard to find which overload is called.On 03.09.2016 10:37, Walter Bright wrote:It's done for arrays via std.array.None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue.Yes they do. It is not possible to implement the range functions as non-members.I don't think it is a template issue. It's a name lookup issue.Nor have I seen ADL supported in any other language, despite many supporting generic algorithms.Which other such languages have templates like D or C++?
Sep 03 2016
On 9/3/2016 6:22 AM, ZombineDev wrote:I agree that it's not a template issue. It's more of a modules vs namespaces everyone does) extend an existing namespace, so most user's of LINQ algorithms just slap a `using System.Linq` on top of the file without caring in exactly which file the functionality they're using is coming from.Hmm, that explanation could be it. I'm not very familiar with LINQ.
Sep 03 2016
On 9/3/16 12:14 PM, Timon Gehr wrote:On 03.09.2016 10:37, Walter Bright wrote:Yah, but I don't see this as an issue. The non-members would need to be in the same module even with ADL, so it's just a clerical matter. -- AndreiNone of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue.Yes they do. It is not possible to implement the range functions as non-members.
Sep 03 2016
On 03.09.2016 03:12, Stefan Koch wrote:On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright wrote:Break how?Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D.I could not agree more strongly! If this feature were supported, it would probably break our module system.Even if we could shoehorn it into the language it would make the compiler slower.If ADL is done as a fallback, then it is only slower in those cases where it is either actually used, or __traits(compiles,...) is used to determine that some function overload does not exist.
Sep 03 2016
On 9/3/2016 3:11 AM, Timon Gehr wrote:If ADL is done as a fallback, then it is only slower in those cases where it is either actually used,That isn't how it works in C++. It's done right up front in finding the candidates for overloading, not as a fallback. Given Manu's other posts where he wants a generic template to be used as the fallback, I don't think ADL as a fallback will work for him, either.
Sep 03 2016
On 9/3/2016 3:35 AM, Walter Bright wrote:That isn't how it works in C++. It's done right up front in finding the candidates for overloading, not as a fallback.That statement is incorrect. It's used as a fallback in C++. I had forgotten.
Sep 03 2016
On Saturday, 3 September 2016 at 10:11:05 UTC, Timon Gehr wrote:If ADL is done as a fallback, then it is only slower in those cases where it is either actually used, or __traits(compiles,...) is used to determine that some function overload does not exist.True. Still it does complicate the implementation. AFAICS the point of ADL is importing function definitions automatically if they are referenced, thereby practically circumventing the guarantees imports give you. In particular : "I will only import what is in that module, and I will only transitively import what is imported publicly by this module" now it becomes : "I will import what is in that module, transitively import public imports, and maybe more if a function called from that module requires it." Please do correct me if my interpretation is wrong. I haven't heard of adl before this post.
Sep 03 2016
On 3 September 2016 at 11:09, Walter Bright via Digitalmars-d <digitalmars-d puremagic.com> wrote:First solution: module bob; struct S { void f(); }This is my current workaround. I'm not happy with it at all. UFCS exists for a reason.Second solution: module user_code; import bob, joe; import myalgorithm; mixin myalgorithm.test!S; mixin myalgorithm.test!T; void main() { test(S.init); test(T.init); }You're not serious, right? I just want to call a function... functions should be functions, not mixin templates!Third solution: module myalgorithm; void test(M,T)(T t) { M.f(t); } module user_code; import bob, joe; import myalgorithm; void main() { test!bob(S.init); test!joe(T.init); }Another crazy workaround. Users should not be expected to manually pass scope's around the place to perform a name lookup. Try and explain that to a normal programmer.Fourth solution: module myalgorithm; void test(T)(T t) { import std.traits; mixin("import " ~ std.traits.moduleName!T ~ ";"); mixin("alias M = " ~ std.traits.moduleName!T ~ ";"); // The above could be encapsulated into an eponymous template // that takes T as a parameter and returns the alias M.f(t); } was rather cool, I plan to use it as an example.I also had this idea as workaround, but you can't seriously think this is okay? Importing an entire module at the point I want to call a function is crazy. I don't want to import _everything_ from T's module into my local namespace; that could easily lead to conflicting names in the local scope which would now require disambiguation. This surely represents a far higher probability of name collisions than the theoretical accidental collision that could come from ADL. The ADL style collision isn't accidental though, that's _the whole point_.D requires ADL so much more than C++ does, because the things ADL does in C++ are absolute concrete advertised core value propositions of D.or had any problems with ADLhttps://en.wikipedia.org/wiki/Argument-dependent_name_lookup#Criticism Essentially, ADL has awkward problems when getting beyond the simple cases. It isn't right for D.ADL has the problems I provided a link to.It's never caused me a problem, in like, 15 years or more. This situation in D causes me problems all the time.In any case, these difficulties are the consequence of trying to write C++ code in D.You've told me this before, and I find it kind of offensive every time you do, since I've been programming D for like 7 years now, and I definitely don't code D like C++. If anything, I have a strong tendency to code C++ like D, and that has lead to a lot of interesting changes in my C++ style. I'm tired of these sorts of dismissals. You insist that I'm not a 'real' D programmer, or something to that effect. This problem consistently arises when I try to commit to go all in on ranges+UFCS pipeline style programming. There's nothing C++-ey about that. C++ can barely do it. It is the advertised mission of modern D programmers to write code this way, and it's the exact area where the problem I'm trying to express breaks down. The interesting part is, when I do try and code this way in C++ (which is brutal, it's all SFINAE based template constraints and stuff), it actually _works_, simply because ADL works. Pipeline programming is a core value proposition for D, as are uber-powerful templates which leads to making algorithms out of everything.None of the algorithms used in std.algorithm or elsewhere in Phobos have this particular issue. Nor have I seen ADL supported in any other language, despite many supporting generic algorithms.std.algorithm is extremely simple, it doesn't do anything except raw algorithm-ey stuff. It doesn't attempt to invoke functionality on the data it's working on. Right now I'm working on image processing. There are lots of image data types, and they all have things like interpolation and blending functions. Write an image processing algorithm that calls out to lerp or blend, and you'll run into these problems instantly. I was writing some audio software some time back, again, trying to use stream processing extensively because it's a perfect match for that workload, but same problem! Write an algorithm that does _work_, rather than does algorithm logic, and you can't miss this problem. You need to call associated functions to do work.I do understand trying to write C++ code in D, because my early FORTRAN programs looked like BASIC, my early C programs looked like FORTRAN, my C++ code looked like C, etc.Again, stop this. It's insulting, and it really pisses me off. I'm writing textbook modern D, which is code that you can't even express in C++.What I have provided is a generic way to make ADL work in D, which shows how adaptable it is.I never said D wasn't adaptable and capable of madhax. I understand very well that D is powerful enough to find a possible work-around for basically anything. You wouldn't believe some of the hacks I'm responsible for! (mostly relating to ref! >_<) It's useful once in a while, but once some particular hax present themselves as rule rather than the excepsion, you find yourself effectively engaged in writing boilerplate. Just like C, but a different kind of boilerplate, and for my money; extremely more complex boilerplate than C/C++, which only experts can read and understand. I am absolutely not okay with that sort of madhax.
Sep 03 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:// Find module in which T was defined template ModuleOf(alias T) { import std.traits : moduleName; mixin("import " ~ moduleName!T ~ ";"); mixin("alias ModuleOf = " ~ moduleName!T ~ ";"); } The import is scoped inside ModuleOf, and so doesn't cause collisions. Besides, import foo : bar; only imports the symbol 'bar' from 'foo', no matter how many symbols there are in 'foo'.Fourth solution: module myalgorithm; void test(T)(T t) { import std.traits; mixin("import " ~ std.traits.moduleName!T ~ ";"); mixin("alias M = " ~ std.traits.moduleName!T ~ ";"); // The above could be encapsulated into an eponymous template // that takes T as a parameter and returns the alias M.f(t); } was rather cool, I plan to use it as an example.I also had this idea as workaround, but you can't seriously think this is okay? Importing an entire module at the point I want to call a function is crazy. I don't want to import _everything_ from T's module into my local namespace; that could easily lead to conflicting names in the local scope which would now require disambiguation. This surely represents a far higher probability of name collisions than the theoretical accidental collision that could come from ADL. The ADL style collision isn't accidental though, that's _the whole point_.Write an algorithm that does _work_, rather than does algorithm logic, and you can't miss this problem. You need to call associated functions to do work.Why does no other language adopt ADL? ADL has been around in C++ for 25 years at least. Or maybe I missed that it does exist in other languages? (The usual way I've seen associated functions made available to generic algorithms is via alias parameters or lambdas that enclose the calls to those functions.)
Sep 03 2016
On Saturday, 3 September 2016 at 09:31:59 UTC, Manu wrote:std.algorithm is extremely simple, it doesn't do anything except raw algorithm-ey stuff. It doesn't attempt to invoke functionality on the data it's working on. Right now I'm working on image processing. There are lots of image data types, and they all have things like interpolation and blending functions. Write an image processing algorithm that calls out to lerp or blend, and you'll run into these problems instantly. I was writing some audio software some time back, again, trying to use stream processing extensively because it's a perfect match for that workload, but same problem! Write an algorithm that does _work_, rather than does algorithm logic, and you can't miss this problem. You need to call associated functions to do work.I have had problems with not having C++ style ADL before, but in the end I'm much happier without it. At the risk of repeating previous posts, Is it specifically this? auto someAlgorithm(T0, T1)(T0 arg0, T1 arg1) { // do some work blend(arg0, arg1); // more work } And you want some way for the author of the types being passed in to define an overload of blend somewhere else that that someAlgorithm is not explicitly aware of? What possible lookup rules would you want to make that work? I can think of a lot of different schemes depending on what you want/need for the situation. D goes the simple, conservative route by default and just looks in progressively wider scopes, which is uncontroversial and good enough in most cases. More complicated cases can be done with various other methods, all of which will then necessarily involve some explicit choice (e.g. passing the scope, passing the function, importing scope etc...) in order to know which one you're using. This seems to me to be a good thing? P.s. your ordinary programmer argument: can you imagine that same ordinary programmer understanding how to properly use and avoid abusing C++ lookup rules?
Sep 03 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:std.algorithm is extremely simple,That's actually a nice compliment! (Though credit for that goes to Andrei, not me.)it doesn't do anything except raw algorithm-ey stuff. It doesn't attempt to invoke functionality on the data it's working on.Sure it does, usually via a lambda passed to it. Recall that C++ ADL predates C++ lambdas by more than a decade, which may explain why C++ has come to rely on ADL.
Sep 03 2016
On 9/3/2016 2:31 AM, Manu via Digitalmars-d wrote:I'm tired of these sorts of dismissals. You insist that I'm not a 'real' D programmer, or something to that effect.Not at all. No insult was intended. People often find better, more D idiomatic ways of writing code and tell me I need to change my style to use it. For example, there was a long discussion about the right way to use trusted in templates a while back. The way I had considered "best practice" was inferior, and I simply could no longer defend it and adopted the way others had developed. Just because I have been writing D longer than anyone doesn't mean I automatically am imbued with wisdom of the best way to do things. We're all learning. The way I write D code constantly evolves. If we're going to find the best way to do things in D, we're going to have to be willing to check our pride at the door and be willing to reexamine any assumption and practice, no matter how long it has been in use. My purpose in participating in this thread is to help you be successful using D, not denigrate you. I'm fully aware that I'm often rather artless in the way I make points, social skills are one of my challenges. So I ask you to be tolerant of my shortcomings in that area, and try to look past it.
Sep 03 2016
On 9/3/16 11:31 AM, Manu via Digitalmars-d wrote:There's really no need to take offense here. We have a bit of a mimosa culture of easily bruised egos it seems. As the joke goes: "You take offense too easily." "I can't believe you just said that!" It's hard to not see your view as coming straight from the "I want to speak Italian by replacing English words with Italian words" box. * It hypothesizes that one entire style of design is completely impossible because it lacks one obscure feature from C++. * Vast evidence to the contrary is ignored. * The fact that the feature is problematic and controversial even within C++ circles is also neglected. * The lack of said feature is regarded as an utter disaster and no workaround is even close to cutting the mustard. * A language change is a must; no library solution would ever be acceptable. For the most part this is a Pop a level up and figure what the needed accomplishment is, so other routes than the ADL bottleneck are possible. Let's approach this together like a problem that has a solution within D, and let's make that solution accessible comfortably. Two apocryphal anecdotes: Koenig invented the eponymous lookup as a consequence to the fact that namespaces broke the "Hello, world" program (something to do with cout and endl being migrated to namespace std), which surprised the inner circles of C++ the way putting mentos in coke surprises the unwary. Namespaces are an unusually poorly designed feature of C++, not second even to exceptions. By that time significant work had been invested in defining and implementing namespaces, so going back wasn't quite an option. ADL was hailed as a horrible hack but a lifesaving one. Compilers have been slow to implement ADL, partly because essentially it meant breaking a lookup algorithm that was relatively orderly and DWIM, and replace it with a patchwork of special cases. The community has since learned to live with the odd idioms that make code work with the new rules. Second anecdote: Scott Meyers had an infamous talk on C++ in which the leitmotif was "What does f(x) mean?" Scott asks the question in the beginning to which the obvious response is "Call function f passing it argument x". By the time his talk is done, the horrified audience realizes they have no idea what f(x) means and where it goes. AndreiYou've told me this before, and I find it kind of offensive every time you do, since I've been programming D for like 7 years now, and I definitely don't code D like C++. If anything, I have a strong tendency to code C++ like D, and that has lead to a lot of interesting changes in my C++ style. I'm tired of these sorts of dismissals. You insist that I'm not a 'real' D programmer, or something to that effect.In any case, these difficulties are the consequence of trying to write C++ code in D.
Sep 03 2016
On 4 September 2016 at 07:38, Andrei Alexandrescu via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/16 11:31 AM, Manu via Digitalmars-d wrote:And you did it again. It's the insistence that I'm trying to write C++ in D that's really annoying. (it's not the first time) I'm writing textbook modern D code. It's a style of design that is absolutely impossible in C++, ergo, I'm writing D code! The thing I'm trying to present is that it doesn't work intuitively. The whole reason I came here to comment is because I realised that D's central modern design pattern, that is, algorithms + UFCS (ie, pipeline style; *impossible in C++*) doesn't quite work properly, and it can be hard to understand why. UFCS needs to 'work', not 'sometimes work'. Let's forget I ever said C++ or ADL. I only raised it because I wondered why this wasn't a problem in C++, and then realised that ADL existed in C++.There's really no need to take offense here. We have a bit of a mimosa culture of easily bruised egos it seems. As the joke goes: "You take offense too easily." "I can't believe you just said that!" It's hard to not see your view as coming straight from the "I want to speak Italian by replacing English words with Italian words" box. * It hypothesizes that one entire style of design is completely impossible because it lacks one obscure feature from C++.You've told me this before, and I find it kind of offensive every time you do, since I've been programming D for like 7 years now, and I definitely don't code D like C++. If anything, I have a strong tendency to code C++ like D, and that has lead to a lot of interesting changes in my C++ style. I'm tired of these sorts of dismissals. You insist that I'm not a 'real' D programmer, or something to that effect.In any case, these difficulties are the consequence of trying to write C++ code in D.* Vast evidence to the contrary is ignored.To the contrary of what? That UFCS from within a template doesn't work in a practical way? Nobody has presented any solution or workaround that doesn't require manual user intervention. A solution like: `adl(string fn, T, Args...)(auto ref T x, auto ref Args args)` has appeared on the table. I find it hard to accept criticism for my opinion when that's the proposed solution. That is a clear demonstration of the problem, not a solution. The logical conclusion of this is that all algorithm implementations have to call all functions this way if you want UFCS to work the way users would expect... but users would not expect to call functions in this way either.* The fact that the feature is problematic and controversial even within C++ circles is also neglected.I haven't commented in ADL's merits in C++, just that it's the mechanism by which C++ doesn't exhibit this (similar) problem which is much more significant in D than it is in C++. D needs a solution otherwise UFCS appears a weak language offering where it breaks down when used in algorithm functions, and I quite strongly resist the idea that the best solution is manual user intervention.* The lack of said feature is regarded as an utter disaster and no workaround is even close to cutting the mustard.I do think it's an absolute disaster. It's a core premise of modern D, algorithms and UFCS are central. I'd almost say they *are* modern D. I don't think core language offerings and design recommendations should require 'work arounds', that sounds like something that's just broken by design. You're right, that doesn't cut the mustard, and I'd be extremely disappointed to accept that as 'solved'.* A language change is a must; no library solution would ever be acceptable.Library solution implies user-intervention. My argument is that I find the notion of user intervention in this case unacceptable. I think it's too fundamental to require work-arounds. I'm happy to be proven wrong, but I'm not gonna concede on that point easily.For the most part this is a Pop a level up and figure what the needed accomplishment is, so other routes than the ADL bottleneck are possible. Let's approach this together like a problem that has a solution within D, and let's make that solution accessible comfortably.Sure... but that doesn't mean I'm just going to accept the proposed workarounds as 'fixed'. I find the proposed workarounds really unpleasant, and I don't want to see that proliferate. I'd rather resist an idea I hate now, than see it take root. If I back off, and they become a thing, then who do I have to blame?Two apocryphal anecdotes: Koenig invented the eponymous lookup as a consequence to the fact that namespaces broke the "Hello, world" program (something to do with cout and endl being migrated to namespace std), which surprised the inner circles of C++ the way putting mentos in coke surprises the unwary. Namespaces are an unusually poorly designed feature of C++, not second even to exceptions. By that time significant work had been invested in defining and implementing namespaces, so going back wasn't quite an option. ADL was hailed as a horrible hack but a lifesaving one. Compilers have been slow to implement ADL, partly because essentially it meant breaking a lookup algorithm that was relatively orderly and DWIM, and replace it with a patchwork of special cases. The community has since learned to live with the odd idioms that make code work with the new rules.I understand these things, but D isn't C++ and modules aren't namespaces. I think something that might appear ADL-like would look different and be simpler in D. An algorithm that calls a function on some T it receives just wants to look near the T; UFCS functions will be there. What you're effectively saying is they introduced ADL as a hack because the language was impractical without it. I'm saying the same situation exists in D right now. I don't think something ADL-like would have the same extent of negative impact on D as it does in C++. Modules are well-defined, and 'looking beside T for UFCS functions' is a much narrower and more well defined search scope.Second anecdote: Scott Meyers had an infamous talk on C++ in which the leitmotif was "What does f(x) mean?" Scott asks the question in the beginning to which the obvious response is "Call function f passing it argument x". By the time his talk is done, the horrified audience realizes they have no idea what f(x) means and where it goes.I've seen this one. Again, we're not talking about C++. It hasn't been explored (to my knowledge) how a similar mechanism it would look and affect D. I suspect (with no evidence) it would be relatively benign by comparison to the problems ADL introduces to C++, and D stands to gain a lot more from the transaction, ie, UFCS will work in generic functions the same as non-generic functions. D requires this much more than C++ does, particularly when you take this as the direction of forward momentum for D design.
Sep 04 2016
On 05.09.2016 06:05, Manu via Digitalmars-d wrote:An algorithm that calls a function on some T it receives just wants to look near the T; UFCS functions will be there.I agree with your post except for this. In general there could be four modules: one defines the type, one defines the UFCS function, one defines the generic algorithm, and the last one imports the former three. The problem is that D has no standard way to make them work together.
Sep 05 2016
On 9/5/16 11:43 AM, Timon Gehr wrote:On 05.09.2016 06:05, Manu via Digitalmars-d wrote:Do you think there should? And if so, what would the algorithm be? -- AndreiAn algorithm that calls a function on some T it receives just wants to look near the T; UFCS functions will be there.I agree with your post except for this. In general there could be four modules: one defines the type, one defines the UFCS function, one defines the generic algorithm, and the last one imports the former three. The problem is that D has no standard way to make them work together.
Sep 05 2016
On Monday, 5 September 2016 at 13:35:02 UTC, Andrei Alexandrescu wrote:On 9/5/16 11:43 AM, Timon Gehr wrote:I ran into that very problem many time, and I think it is legitimate. Typical use case is a type from module A that is extended by module B to conform to whatever typeclass is desired (a range for instance) via UFCS. It is not possible to pass it down to anything generic expecting that typeclass is not passed down. However, it is not clear what the solution should be. Tweaking the identifier resolution would make template instance dependent on the instantiation point, which is a non starter (it would cause an explosion is both resources required to compile and a huge amount a binary bloat). On the other hand, I was wondering if it is possible to create a wrapper type type, with alias this, that is dependent on the scope, and that do UFCS resolution is that scope. One could then use the template like : myalgorithm(forwardUFCS(var)); With var the vairable of the type extended to fit the typeclass. I suspect this can be done with a fair amount of mixin magic, but I'm not 100% sure. This way, forwarding the instancier scope would only be done on demand, which would mitigate the instanciation explosion problem one face when doing it as identifier resolution level.On 05.09.2016 06:05, Manu via Digitalmars-d wrote:Do you think there should? And if so, what would the algorithm be? -- AndreiAn algorithm that calls a function on some T it receives just wants to look near the T; UFCS functions will be there.I agree with your post except for this. In general there could be four modules: one defines the type, one defines the UFCS function, one defines the generic algorithm, and the last one imports the former three. The problem is that D has no standard way to make them work together.
Sep 05 2016
On 05.09.2016 15:35, Andrei Alexandrescu wrote:On 9/5/16 11:43 AM, Timon Gehr wrote:Probably. This kind of pattern has been very successful in other languages.On 05.09.2016 06:05, Manu via Digitalmars-d wrote:Do you think there should?An algorithm that calls a function on some T it receives just wants to look near the T; UFCS functions will be there.I agree with your post except for this. In general there could be four modules: one defines the type, one defines the UFCS function, one defines the generic algorithm, and the last one imports the former three. The problem is that D has no standard way to make them work together.And if so, what would the algorithm be? -- AndreiThis is a good question. I'm not sure yet what the best solution is. One hacky way is to provide a mixin template to create a wrapper type within each module that needs it, with std.typecons.Proxy. Proxy picks up UFCS functions in addition to member functions and turns them into member functions. But this leads to a lot of template bloat, because callers that share the same added UFCS functions don't actually share the instantiation. Also, it only works one level deep and automatically generated Wrapper types are generally prone to be somewhat brittle. There is always the default option of requiring the user to create the Wrappers manually, but that's a lot of boilerplate. (Other languages can do analogous things in a very streamlined fashion, by not supporting arbitrary template constraints.)
Sep 05 2016
On Monday, 5 September 2016 at 23:50:33 UTC, Timon Gehr wrote:One hacky way is to provide a mixin template to create a wrapper type within each module that needs it, with std.typecons.Proxy. Proxy picks up UFCS functions in addition to member functions and turns them into member functions. But this leads to a lot of template bloat, because callers that share the same added UFCS functions don't actually share the instantiation. Also, it only works one level deep and automatically generated Wrapper types are generally prone to be somewhat brittle.I don't think cloning a type just to add functionality can possibly be the right way. A C++-style of customizing behavior is using traits. Those traits would be a compile time argument to the algorithm function. Instead of arg.addone() one would use trait.addone(arg). It is not hard to write a proxy that merges trait and arg into one entity, but this should to be done from the callee. The default trait would be type.addone_trait if it exists, or else some default trait that uses all available functions and member function from the module of the type. In most of the cases this is enough, but it enables adding traits to existing types and also different implementations of the same traits. This gets really bloaty in C++, and that's why usually ADL is preferred, but D has the capability to reduce the overhead to a minimum. It doesn't quite make it possible to separate the implementation of types, algorithms and traits (UFCS) into different modules such that they don't know each other. Either the user has to specify the trait each call or either the type's module or the algorithm's module has to import the traits. What I call traits is very similar to type classes in other languages where (among other features) the traits are automatically being attached to the type. (Type classes are also what C++ concepts originally wanted to be.)
Sep 06 2016
On 5 September 2016 at 14:05, Manu <turkeyman gmail.com> wrote:I've seen this one. Again, we're not talking about C++. It hasn't been explored (to my knowledge) how a similar mechanism it would look and affect D. I suspect (with no evidence) it would be relatively benign by comparison to the problems ADL introduces to C++, and D stands to gain a lot more from the transaction, ie, UFCS will work in generic functions the same as non-generic functions. D requires this much more than C++ does, particularly when you take this as the direction of forward momentum for D design.Anyway, I've made my case. I'm watching to see where this goes. I don't really have anything to add, so I'll let it be from here.
Sep 04 2016
On 9/3/16 3:09 AM, Walter Bright wrote: [snip] What would be really nice is to allow ADL easily and without fuss when needed. On Manu's example: module bob; struct S {} void f(S s); module joe; struct T {} void f(T t); module myalgorithm; void test(T)(T t) { mixin(adl!(T, "f")); f(t); } So adl!(T, "f") expands to an import of f from T's module if it defines a function f, or nothing if it doesn't. Generally I agree that there's more upside to not introducing ADL for D. Andrei
Sep 03 2016
On Saturday, 3 September 2016 at 01:09:18 UTC, Walter Bright wrote:Fourth solution: module myalgorithm; void test(T)(T t) { import std.traits; mixin("import " ~ std.traits.moduleName!T ~ ";"); mixin("alias M = " ~ std.traits.moduleName!T ~ ";"); // The above could be encapsulated into an eponymous template // that takes T as a parameter and returns the alias M.f(t); }Chipping in to say that I currently do something this with Binderoo templates... and it sucks. https://github.com/Remedy-Entertainment/binderoo/blob/master/binderoo_client/d/src/binderoo/variabledescriptor.d One example is in there, the VariableDescriptors eponymous template, where a template that collects every member variable of an object has to mix in the module names of each encountered member variable type to stop the compiler complaining about module visibility. So I'm doing the double whammy of taxing the template expansion engine and the CTFE engine. It could be that switching it to a mixin template (and working out someway to make it as usable as eponymous templates) will solve the problem - but the way this codebase is going it's going to mean every template needs to be a mixin. Surely the base template system can be more flexible than this?
Sep 04 2016
On 9/4/2016 1:48 PM, Ethan Watson wrote:Chipping in to say that I currently do something this with Binderoo templates... and it sucks.What about using this template? // Find module in which T was defined template ModuleOf(alias T) { import std.traits : moduleName; mixin("import " ~ moduleName!T ~ ";"); mixin("alias ModuleOf = " ~ moduleName!T ~ ";"); } and it only has to be written once. Yes, it uses CTFE and mixins, but it's all hidden away inside the template.
Sep 04 2016
On Monday, 5 September 2016 at 01:00:26 UTC, Walter Bright wrote:What about using this template?Sure, it'll work assuming the module imports all its symbols publically, but it's still not as usable as it should be. I still need to invoke it for a number of things, including member variable types. If the member variable is templated, I need to analyse the template arguments for types to import them too. If it is a function, I need to treat each argument as I treat a member variable. I started a thread the other day that touches on another problem I have which this template won't solve: https://forum.dlang.org/thread/wggldyzrbwjboibinuwj forum.dlang.org At least in my use cases, it comes down to the template instance not inheriting the visibility of symbols from its template parameters. Which leads to these workarounds. We're aiming for the goal of sub-second compile and reload times for rapid iteration, both with normal code and scripter code. Anything I have to do through templates and CTFE slows compile times down, in some cases significantly.
Sep 05 2016
On Friday, 2 September 2016 at 23:51:35 UTC, Manu wrote:This pattern seems to bite me every direction I turn when trying to write range or algorithm style code. C++ has ADL, and ADL works. I've never thought about this problem in C++, or had any problems with ADL.IMO the root of this problem is that templates are *duck typed*. All those problems wouldn't even exist with concepts/traits/typeclasses (done right). ADL is only an ugly hack. And with "Design by Introspection" it only gets worse: If an optional operation exists, but is not found because of unexpected problems like these, it still compiles but you only get limited functionality or bad performance.
Sep 03 2016
On 9/3/16 10:01 AM, Tobias Müller wrote:On Friday, 2 September 2016 at 23:51:35 UTC, Manu wrote:What problems are you referring to? -- AndreiThis pattern seems to bite me every direction I turn when trying to write range or algorithm style code. C++ has ADL, and ADL works. I've never thought about this problem in C++, or had any problems with ADL.IMO the root of this problem is that templates are *duck typed*. All those problems wouldn't even exist with concepts/traits/typeclasses (done right). ADL is only an ugly hack. And with "Design by Introspection" it only gets worse: If an optional operation exists, but is not found because of unexpected problems like these, it still compiles but you only get limited functionality or bad performance.
Sep 03 2016
On Saturday, 3 September 2016 at 12:25:11 UTC, Andrei Alexandrescu wrote:What problems are you referring to? -- AndreiThe problems discussed here in the thread related to name lookup at template instantiation time. And also similar problems related to visibility (public/private) that were discussed in a different thread recently.
Sep 03 2016
On 9/3/16 4:09 PM, Tobias M wrote:On Saturday, 3 September 2016 at 12:25:11 UTC, Andrei Alexandrescu wrote:I see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- AndreiWhat problems are you referring to? -- AndreiThe problems discussed here in the thread related to name lookup at template instantiation time. And also similar problems related to visibility (public/private) that were discussed in a different thread recently.
Sep 03 2016
On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:I see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- AndreiThe problem I see with DbI is rather that the user of a function thinks that an optional constraint is satisfied, while in reality it isn't, due to a non-obvious lookup/visibility problem.
Sep 03 2016
On 9/3/16 7:08 PM, Tobias M wrote:On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:At some point there's a need to RTFM. -- AndreiI see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- AndreiThe problem I see with DbI is rather that the user of a function thinks that an optional constraint is satisfied, while in reality it isn't, due to a non-obvious lookup/visibility problem.
Sep 03 2016
Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:On 9/3/16 7:08 PM, Tobias M wrote:Is there one for DbI? (Sincere question)On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:At some point there's a need to RTFM. -- AndreiI see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- AndreiThe problem I see with DbI is rather that the user of a function thinks that an optional constraint is satisfied, while in reality it isn't, due to a non-obvious lookup/visibility problem.
Sep 03 2016
On 9/3/16 7:38 PM, Tobias Müller wrote:Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:Not yet. We have the allocators body of work, but that's too niche to serve as a general example. I think std.experimental.checkedint will be the canonical example on how to do DbI. I'll propose a blog post to Mike. -- AndreiOn 9/3/16 7:08 PM, Tobias M wrote:Is there one for DbI? (Sincere question)On Saturday, 3 September 2016 at 16:33:07 UTC, Andrei Alexandrescu wrote:At some point there's a need to RTFM. -- AndreiI see. This is a matter orthogonal to DbI - introspection should be able to figure out whether a member can be found, or a nonmember if the design asks for it. I wouldn't like "tricking" DbI into thinking a member is there when there isn't. -- AndreiThe problem I see with DbI is rather that the user of a function thinks that an optional constraint is satisfied, while in reality it isn't, due to a non-obvious lookup/visibility problem.
Sep 03 2016
On 9/3/16 1:51 AM, Manu via Digitalmars-d wrote:I've never thought about this problem in C++, or had any problems with ADL.How do you swap two objects of a generic type that may or may not define its own swap? -- Andrei
Sep 03 2016
On 3 September 2016 at 22:42, Andrei Alexandrescu via Digitalmars-d <digitalmars-d puremagic.com> wrote:On 9/3/16 1:51 AM, Manu via Digitalmars-d wrote:It's not a problem I've ever had. I'm not actually quite sure I understand your question... I guess this is sort of like the issue I was alluding to in my other thread though; there exists a generic implementation, but some type requires to specialise for itself, which then needs to trump the otherwise ambiguous conflict with the catch-all? I think that's an interesting problem, but it's quite easy to solve with an (ugly) forwarding template; but that forwawrding template leads right back here, where the worker `doThingImpl()` as implemented for each type that supports 'doThing' depends on ADL to be callable from the master `doThing()` function.I've never thought about this problem in C++, or had any problems with ADL.How do you swap two objects of a generic type that may or may not define its own swap? -- Andrei
Sep 03 2016
On 9/3/16 5:57 PM, Manu via Digitalmars-d wrote:On 3 September 2016 at 22:42, Andrei Alexandrescu via Digitalmars-d <digitalmars-d puremagic.com> wrote:A problem you didn't know you have. It's a classic C++ conundrum combining theory and practice.On 9/3/16 1:51 AM, Manu via Digitalmars-d wrote:It's not a problem I've ever had.I've never thought about this problem in C++, or had any problems with ADL.How do you swap two objects of a generic type that may or may not define its own swap? -- AndreiI'm not actually quite sure I understand your question...The task is to swap two objects of generic type T. If T's namespace defines an overload of swap with the appropriate signature, use it. Otherwise, fall back to std::swap. For bonus points: if T defines swap as a _member_ function, use that.I guess this is sort of like the issue I was alluding to in my other thread though; there exists a generic implementation, but some type requires to specialise for itself, which then needs to trump the otherwise ambiguous conflict with the catch-all? I think that's an interesting problem, but it's quite easy to solve with an (ugly) forwarding template; but that forwawrding template leads right back here, where the worker `doThingImpl()` as implemented for each type that supports 'doThing' depends on ADL to be callable from the master `doThing()` function.Post the code. Andrei
Sep 03 2016
On 9/3/2016 9:24 AM, Andrei Alexandrescu wrote:On 9/3/16 5:57 PM, Manu via Digitalmars-d wrote:One thing I've noticed in my years of programming and helping others with their code is that one can use a feature for decades, over and over, but are only using it in a certain specific way. One is lulled into thinking that it works in the general case because one is not aware that one's usage is constrained. Someone else comes along, uses it slightly differently, and oops. "But I always took the left fork!" :-) It bites me all the time.It's not a problem I've ever had.A problem you didn't know you have. It's a classic C++ conundrum combining theory and practice.
Sep 03 2016