UFCS in generic libraries, silent hijacking, and compile errors.

aliak something at something.com
Sun Mar 11 08:39:54 UTC 2018


On Saturday, 10 March 2018 at 23:00:07 UTC, Jonathan M Davis 
wrote:
>
> The idea is that the type can provide its own version of the 
> function that is better optimized for it - e.g. it could 
> potentially provide a member function find that is more 
> efficient for it than std.algorithm.searching.find. That's 
> actually the only technical reason why UFCS is superior to the 
> normal function call syntax.

I think this may have hit the nail on the spot. So basically my 
thinking is that if you're going to use UFCS inside a generic 
function where you can't know what kind of methods the type has, 
do not do it unless you want this particular behavior or if your 
intent is to use a type's known API.

> issue in practice. That doesn't mean that it's never a problem, 
> but from what I've seen, it's very rarely a problem, and it's 
> easy to work around if you run into a particular case where it 
> is a problem.

Ya, it's easy to work around but the caveat there is you need to 
realize it's happening first, and add that to that it's "rarely a 
problem" and well ... now it seems scary enough for this to 
mentioned somewhere I'd say.

>
> The one case that I am aware of where best practice is to avoid 
> UFCS is with put for output ranges, but that has nothing to 
> with your concerns here. Rather, it has to do with the fact 
> that std.range.primitives.put has a lot of overloads for 
> handling various arguments (particularly when handling ranges 
> of characters), and almost no one implements their output 
> ranges with all of those overloads. So, if you use put with 
> UFCS, you tend to run into problems if you do anything other 
> than put a single element of the exact type at a time, whereas 
> the free function handles more cases (even if they ultimately 
> end up calling that member function with a single argument of 
> the exact type). We probably shouldn't have had the free 
> function and the member function share the same name.

Oh, can you share a short example here maybe? Not sure I followed 
completely

Is it basically:

// if r is output range

r.put(a, b) // don't do this?

put(r, a, b) // but do this?

(Cause compilation error)


>
>> Is there something I'm not seeing as to why UFCS is not part 
>> of the overload set, and is there a way other than not using 
>> UFCS to prevent the silent hijacking?
>
> If it were part of the overload set, then you have problems 
> calling member functions, particularly because there is no way 
> to call a member function other than with UFCS, whereas you can 
> call free functions without UFCS, and if you _really_ want to 
> be sure that a very specific function is called, then you can 
> even give its entire module path when calling it. When UFCS was 
> introduced, it was decided that having member functions always 
> win out would cause the fewest problems.
>
> - Jonathan M Davis

Ah yes, ok this makes sense.

Follow up:

How about if it's not part of the overload set, but is looked up 
if the function does not exist in the overload set. What would 
the problems there be?

Basically I don't see a reason why we wouldn't want the following 
to work:

struct S { void f() {} }
void f(S s, int i) {}
S().f(3); // error

Thanks!




More information about the Digitalmars-d-learn mailing list