www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - free func "front" is not picked up as a candidate when doing

reply aliak <something something.com> writes:
Hi, I'm trying to make a range.front free function that can be 
given a defaultValue. Doesn't seem to be working as is written 
below, seems like the compiler doesn't see the free function as a 
viable candidate. Isn't it supposed to do its UFCS wizardry and 
pick up the free func?

import std.stdio;

auto front(Range, T)(Range range, T defaultValue) {
     import std.range: empty, front;
     return range.empty ? defaultValue : range.front;
}

void main()
{
     import std.range: iota;
     0.iota.front(100).writeln;
// Error: inout method std.range.iota!(int, 
int).iota.Result.front is not callable using a mutable object

     import std.algorithm: filter;
     auto arr = [0, 1];
     arr.filter!"true".front(99).writeln;
// Error: function 
std.algorithm.iteration.FilterResult!(unaryFun, 
int[]).FilterResult.front () is not callable using argument types 
(int)

     arr.front(30).writeln; // OK

     import std.range: front;
     arr.front(30).writeln;
// Error: template std.range.primitives.front cannot deduce 
function from argument types !()(int[], int), candidates are: 
std.range.primitives.front(T)(T[] a) ...
}

Cheers
Feb 07
parent reply Paul Backus <snarwin gmail.com> writes:
On Wednesday, 7 February 2018 at 19:25:01 UTC, aliak wrote:
     import std.range: iota;
     0.iota.front(100).writeln;
 // Error: inout method std.range.iota!(int, 
 int).iota.Result.front is not callable using a mutable object

     import std.algorithm: filter;
     auto arr = [0, 1];
     arr.filter!"true".front(99).writeln;
 // Error: function 
 std.algorithm.iteration.FilterResult!(unaryFun, 
 int[]).FilterResult.front () is not callable using argument 
 types (int)
You can only call a function with UFCS syntax if the object you're calling it with does not have a member with the same name as the function. Both iota's `Result` type and `FilterResult` have properties named "front", so you can't call your front function on them using UFCS syntax.
Feb 07
parent reply aliak <something something.com> writes:
On Wednesday, 7 February 2018 at 20:08:10 UTC, Paul Backus wrote:
 You can only call a function with UFCS syntax if the object 
 you're calling it with does not have a member with the same 
 name as the function. Both iota's `Result` type and 
 `FilterResult` have properties named "front", so you can't call 
 your front function on them using UFCS syntax.
ahhh... bummer. Do you know if it is a limitation that is yet to be implemented or one that will not (cannot?) be implemented and if so what the reason is?
Feb 07
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, February 07, 2018 20:17:54 aliak via Digitalmars-d-learn 
wrote:
 On Wednesday, 7 February 2018 at 20:08:10 UTC, Paul Backus wrote:
 You can only call a function with UFCS syntax if the object
 you're calling it with does not have a member with the same
 name as the function. Both iota's `Result` type and
 `FilterResult` have properties named "front", so you can't call
 your front function on them using UFCS syntax.
ahhh... bummer. Do you know if it is a limitation that is yet to be implemented or one that will not (cannot?) be implemented and if so what the reason is?
It would be a disaster if free functions could override member functions. For starters, it would be impossible to call the member function if that were allowed, whereas you can always call a free function by not using UFCS. And in general, it's far more desirable to have member function functions override free functions, because then you can do stuff like have have a range override find if it can provide a more efficient implementation than std.algorithm.find. If you want to override what a member function is doing, then you need to wrap the object in a new type. - Jonathan M Davis
Feb 07
parent reply aliak <something something.com> writes:
On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis 
wrote:
 It would be a disaster if free functions could override member 
 functions. For starters, it would be impossible to call the 
 member function if that were allowed, whereas you can always 
 call a free function by not using UFCS. And in general, it's 
 far more desirable to have member function functions override 
 free functions, because then you can do stuff like have have a 
 range override find if it can provide a more efficient 
 implementation than std.algorithm.find.
That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions. Also, in this case where it's an overload, and not an override, it's very clear what should be called isn't it? In the case of an override I "feel" it may indeed be a disaster, but not for the reasons you've stated above though, also not for any reasons I can think of :) Hijacking maybe? But then again, if I have a free function and then decide to make a member function I'm hijacking the free function... hmm. Cheers
Feb 08
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, February 08, 2018 08:18:20 aliak via Digitalmars-d-learn wrote:
 On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis

 wrote:
 It would be a disaster if free functions could override member
 functions. For starters, it would be impossible to call the
 member function if that were allowed, whereas you can always
 call a free function by not using UFCS. And in general, it's
 far more desirable to have member function functions override
 free functions, because then you can do stuff like have have a
 range override find if it can provide a more efficient
 implementation than std.algorithm.find.
That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions.
That's exactly how it works now. If you call a function as if it were a member function, and that type has that member function, then it's that member function that gets called. If that type doesn't have that member function, then free functions are checked.
 Also, in this case where it's an overload, and not an override,
 it's very clear what should be called isn't it?

 In the case of an override I "feel" it may indeed be a disaster,
 but not for the reasons you've stated above though, also not for
 any reasons I can think of :) Hijacking maybe? But then again, if
 I have a free function and then decide to make a member function
 I'm hijacking the free function... hmm.
D has a lot of decisions geared towards avoiding function hijacking, and when UFCS was added to the language, it was very purposefully decided that member functions would win over free functions. It is highly unlikely that that's going to change. Any such change would require a DIP which explained in detail the pros and cons of the change and why it either wouldn't break any existing code or why the code breakage would be worth it and how the transition would work. - Jonathan M Davis
Feb 08
parent reply aliak <something something.com> writes:
On Thursday, 8 February 2018 at 19:32:42 UTC, Jonathan M Davis 
wrote:
 On Thursday, February 08, 2018 08:18:20 aliak via 
 Digitalmars-d-learn wrote:
 On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis

 wrote:
 It would be a disaster if free functions could override 
 member functions. For starters, it would be impossible to 
 call the member function if that were allowed, whereas you 
 can always call a free function by not using UFCS. And in 
 general, it's far more desirable to have member function 
 functions override free functions, because then you can do 
 stuff like have have a range override find if it can provide 
 a more efficient implementation than std.algorithm.find.
That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions.
That's exactly how it works now. If you call a function as if it were a member function, and that type has that member function, then it's that member function that gets called. If that type doesn't have that member function, then free functions are checked.
Wait no, that's not how it works now. Right now if a member function with the same name exists, then free functions are not even checked. Whereas if there's a precedence rule that prefers member functions, then free functions will still be checked if no member function with that name can be deduced as a viable candidate. How it is now disregards free functions without any consideration (if I've understood correctly), whereas I'm asking why free functions are not even considered if a member function is not an actual candidate. I'm not asking about overriding member functions with free functions. i.e. in my original post, the free function front(int) does not exist in type FilterResult. Cheers, - Ali
Feb 08
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, February 08, 2018 21:58:39 aliak via Digitalmars-d-learn wrote:
 On Thursday, 8 February 2018 at 19:32:42 UTC, Jonathan M Davis

 wrote:
 On Thursday, February 08, 2018 08:18:20 aliak via

 Digitalmars-d-learn wrote:
 On Thursday, 8 February 2018 at 07:16:43 UTC, Jonathan M Davis

 wrote:
 It would be a disaster if free functions could override
 member functions. For starters, it would be impossible to
 call the member function if that were allowed, whereas you
 can always call a free function by not using UFCS. And in
 general, it's far more desirable to have member function
 functions override free functions, because then you can do
 stuff like have have a range override find if it can provide
 a more efficient implementation than std.algorithm.find.
That could be fixed with a precedence rule though no? Then member find will be preferred over free find. And it won't be impossible to call member functions.
That's exactly how it works now. If you call a function as if it were a member function, and that type has that member function, then it's that member function that gets called. If that type doesn't have that member function, then free functions are checked.
Wait no, that's not how it works now. Right now if a member function with the same name exists, then free functions are not even checked. Whereas if there's a precedence rule that prefers member functions, then free functions will still be checked if no member function with that name can be deduced as a viable candidate. How it is now disregards free functions without any consideration (if I've understood correctly), whereas I'm asking why free functions are not even considered if a member function is not an actual candidate. I'm not asking about overriding member functions with free functions. i.e. in my original post, the free function front(int) does not exist in type FilterResult.
D tends to be very picky about what it puts in overload sets in order to avoid function hijacking - e.g. it doesn't even include base class functions in an overload set once you've declared one in a derived class unless you explicitly add an alias to the base class function in the derived class. Also, D doesn't support "best match" with function overloads. The matches have to be exact - e.g. as soon as implicit conversions come into play, there can only be one match. If there's ever a conflict, you get a compilation error (unlike in C++, which tries to figure out what it thinks the best match is and go with that, sometimes with surprising results). And having both the member functions and free functions in the same overload set would basically require the compiler to go with the "best match," which simply isn't how D deals with overloads. Sometimes, that's annoying, but it also prevents a lot of subtle bugs that tend to crop up in C++ code. https://dlang.org/spec/function.html#function-overloading https://dlang.org/articles/hijack.html - Jonathan M Davis
Feb 08
parent aliak <something something.com> writes:
On Thursday, 8 February 2018 at 22:57:04 UTC, Jonathan M Davis 
wrote:
 D tends to be very picky about what it puts in overload sets in 
 order to avoid function hijacking - e.g. it doesn't even 
 include base class functions in an overload set once you've 
 declared one in a derived class unless you explicitly add an 
 alias to the base class function in the derived class.

 Also, D doesn't support "best match" with function overloads. 
 The matches have to be exact - e.g. as soon as implicit 
 conversions come into play, there can only be one match. If 
 there's ever a conflict, you get a compilation error (unlike in 
 C++, which tries to figure out what it thinks the best match is 
 and go with that, sometimes with surprising results). And 
 having both the member functions and free functions in the same 
 overload set would basically require the compiler to go with 
 the "best match," which simply isn't how D deals with 
 overloads. Sometimes, that's annoying, but it also prevents a 
 lot of subtle bugs that tend to crop up in C++ code.
 
 https://dlang.org/spec/function.html#function-overloading 
 https://dlang.org/articles/hijack.html

 - Jonathan M Davis
Thanks for the articles, they shed a bit more understanding. But I have some concerns/issues that maybe you or others can help out with a bit more. First, the link on function-overloading says "The function with the best match is selected. " So I'm not sure what you mean about subtle bugs in C++ and D doesn't support best match. If you have foo(long), food(int) and call foo(3) in the same module, the best match is selected in D and in C++. But, anyway, ignoring C++ for now, after reading those articles you linked to my main issues are D related, not C++, let me try to explain: The rules of resolving overload sets seem to be to resolve the issue where two overload sets have a matching function and one is silently better. This is understandable and makes sense. The case of a free function not being considered if there is a member functions seems unrelated (as you say they are not part of the overload set, but it seems like like maybe they should be). In this case the member function does not match. If overload set resolution was applied the free function would be considered and be chosen as the only available candidate. The rules for overriding, where functions with the same name in a base class are hidden, also make sense. But not relevant here. Infact, the hijacking article makes it seem to me that ufcs hijacking has maybe been overlooked? Consider two modules which are unrelated: module a; string f(R)(R r, int i) { return "a.f"; } string g(R)(R r) { return "a.g"; } --------- module b; auto algo() { static struct R { void popFront() {} enum empty = false; int front() { return 1; } } return R(); } ---------- // Application programmer; import std.stdio: writeln; import a: f, g; import b: algo; void main(string[] args) { auto r = algo(); auto a = r.f(3); auto b = r.g; writeln(a); // will print a.f writeln(b); // will print a.g } All good so far. Until module b decides to add an internal function to it's R type: auto algo() { static struct R { // Add a function string g() { return "internal g"; } } return R(); } Application programmer updates module b... auto b = r.g; // Does not do what programmer thinks anymore, and no warnings. Because a.g does not participate in overload set resolution rules, the application programmer has an unknown bug (is it just me or is this a bit... to put it lightly... scary?) So that's the first problem. Then, secondly, say the people who make module b like using proper access control levels, and mark the internal string g() as private. Now there's a compiler error in the application programmer, with no real clues as to why, unless you deep dive in to D lang specs. And then you find out you're getting a compiler error because of a private, internal, function in a Voldermort type... ouch. Like. Big ouch. But ok, this can at least be worked around: import a: f, c = g; Now: auto b = r.c; // ok. Third problem: Module b person adds another internal function to their type. auto algo() { static struct R { // Add a function string f() { return "internal f"; } } return R(); } Now application programmer has a compile error again. Again with no clue. But this time it makes no sense because the line: auto a = r.f(3); Is clearly and unambiguously calling a.f(int). Again here if the free function was part of the overload set resolution rules, it would be resolved correctly. Granted problem two and three are related. But there're subtle semantic difference there. Either this has been overlooked or there's a reason it is like this and must be like this that I've yet to hear. As it is right now (someone correct me if I'm wrong), for someone who is writing libraries, it seems either impractical to use ufcs because of compilation errors that it would cause in client code, or, worse, unsafe because of silent bugs that would occur inside client code AND library code in the case of types adding matching function (i.e. same signature/name) in modules that the library author and application author have no knowledge, or control of. Cheers, - Ali
Feb 11