D UFCS anti-pattern

Steven Schveighoffer via Digitalmars-d digitalmars-d at puremagic.com
Fri Apr 25 05:17:20 PDT 2014


On Fri, 25 Apr 2014 00:58:51 -0400, Jonathan M Davis via Digitalmars-d  
<digitalmars-d at puremagic.com> wrote:

>
> If it doesn't work to override a free function with a member
> function, I honestly don't see much point to UFCS. The whole idea
> behind it is to make it so that you don't have to care whether a
> function is a free function or a member function. The current situation
> essentially forces you to not use UFCS except in cases where you're
> trying to add "member functions" to built-in types. And as such,
> calling functions on user-defined types using UFCS runs a high risk of
> not compiling, because all it takes is for the user-defined type to
> define a function with the same name - even if it takes completely
> different arguments - and now the compiler won't even try to use the
> free function anymore.

Right but the pattern is:

someGlobal(T t, U u)
{
    static if(is(typeof(t.someGlobal(u))))
      t.someGlobal(u);
    else
      // use default implementation.
}

Essentially, someGlobal is encouraging not only overriding itself for a  
type, but for only a PORTION of the implementation.

If you override put completely, allowing all the mechanisms put uses, that  
is absolutely fine. But for only implementing a single part, the hook put  
uses to interface with the type should NOT be named put. This means the  
member put is far less functional than the global function put.

What I'm saying is that the hook and the global function should not be  
named the same. Not that we should change the override rules.

> I really think that we should fix it so that stuff like
> outputRange.put(foo) works - including when types define put
> themselves. AFAIK, that means changing the overload rules so that
> member functions conflict with free functions only when they take the
> same arguments - in which case the member function would be called, as
> it is now, except that the cases where a free function matches the
> arguments would also work, allowing us to override free functions with
> member functions where appropriate and prevent simple name collisions
> from making UFCS not work (i.e. when the member function takes
> completely different arguments, UFCS would still use the free
> function). Without a change along those lines, I'd be strongly inclined
> to argue against using UFCS in any situation except in those where you
> need to add "member functions" to the built-in types. And the only
> common case for that that I'm aware of is making it so that arrays can
> function as ranges.

I don't think this is a good idea. A type first and foremost is in charge  
of its API.

Another possible option is to make sure put(R, ...) is as limited as the  
member R.put(...). This means, if R.put doesn't support the parameters,  
put(R, ...) should also reject them.

This at least is consistent, but I think code will break for not much of a  
good reason.

-Steve


More information about the Digitalmars-d mailing list