www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: overload conflict resolution

reply "Chris Miller" <chris dprogramming.com> writes:
Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

Currently, if you try to do foo("hello"), the compiler will complain  
because string literals simulteneous match all the above types.

Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

foo("hello")  will now select the char[] version because it was marked as  
the default type in the overload.

What it does not do:
    1) Resolve conflicts between different scopes.
    2) Override exact matches.

The way overloads work now is helpful in some cases, but in other cases,  
it's perfectly fine to prefer an overload over another.


Example where you do not want a default overload: working with binary,  
e.g. writing to Stream or Socket, where the wrong type can screw up the  
format or transmission.

Example where you do: custom string object's constructor that wants to  
allow char[], wchar[] and dchar[] types, but wants to default string  
literals to char[].


The compiler would go down the line of parameters of overloads, and upon  
any conflicts, would look for default to resolve them. If default is used  
to resolve conflicts in more that one of the functions, it's an actual  
conflict.

Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

Note: this example probably wouldn't actually be used; as with most  
language constructs, there's a way to abuse it, but this example gets a  
point across that it at least has logic:
bar("hello", 42)  chooses the first one because the string literal  
conflicted and went with the default one.
bar("hello"c, 42)  chooses the second one; the string literal specified  
char[] (with the c) and then had a conflict with the second parameter,  
where default resolved it.


The programmer knows which overloads, if any, can be preferred over  
others, because this feature works only confined within one scope, and so  
it is safe to let him choose for you.


Where this can be useful:
    1) String literals conflicting with char[], wchar[] and dchar[].
    2) null conflicting with string types, delegates, pointers, and class  
references.
    3) Integer literals conflicting with the number types.
    4) Different levels of derived classes conflicting with each other.


Thanks,
- Chris
Dec 10 2006
next sibling parent "Kristian Kilpi" <kjkilpi gmail.com> writes:
On Sun, 10 Dec 2006 16:36:51 +0200, Chris Miller <chris dprogramming.com>  
wrote:
 Given the following function prototypes:
     void foo(char[] s);
     void foo(wchar[] s);
     void foo(dchar[] s);

 Currently, if you try to do foo("hello"), the compiler will complain  
 because string literals simulteneous match all the above types.

 Proposal:
     void foo(default char[] s);
     void foo(wchar[] s);
     void foo(dchar[] s);

I have hoped that something like this would be possible in C++, and now, in D. Gets my vote!
Dec 10 2006
prev sibling next sibling parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
 
 Currently, if you try to do foo("hello"), the compiler will complain 
 because string literals simulteneous match all the above types.
 
 Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

 The compiler would go down the line of parameters of overloads, and upon 
 any conflicts, would look for default to resolve them. If default is 
 used to resolve conflicts in more that one of the functions, it's an 
 actual conflict.
 
 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

I like the basic idea, but maybe it gets a bit to hairy when multiple parameters can have this attribute... Maybe it should be an attribute of the function instead? Then it becomes quite clear, I think: if there are multiple equally-good matches and among them the number of overloads with the attribute is anything other than one (that is, none or at least two) it's still an error, otherwise the one declared 'default' is picked.
Dec 10 2006
next sibling parent reply "Frank Benoit (keinfarbton)" <benoit tionex.removethispart.de> writes:
 
 Maybe it should be an attribute of the function instead?

This gets my vote
Dec 10 2006
parent Lars Ivar Igesund <larsivar igesund.net> writes:
Frank Benoit (keinfarbton) wrote:

 
 Maybe it should be an attribute of the function instead?

This gets my vote

Yes, this might work. -- Lars Ivar Igesund blog at http://larsivi.net DSource & #D: larsivi
Dec 10 2006
prev sibling next sibling parent reply "Rioshin an'Harthen" <rharth75 hotmail.com> writes:
"Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote:
 Chris Miller wrote:
 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

I like the basic idea, but maybe it gets a bit to hairy when multiple parameters can have this attribute... Maybe it should be an attribute of the function instead? Then it becomes quite clear, I think: if there are multiple equally-good matches and among them the number of overloads with the attribute is anything other than one (that is, none or at least two) it's still an error, otherwise the one declared 'default' is picked.

So, we'd have something like default void foo(char[] s); void foo(wchar[] s); void foo(dchar[] s); with only one function of the same name at most with default... I like this :). Votes++ for this modification.
Dec 10 2006
parent reply Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Rioshin an'Harthen wrote:
 "Frits van Bommel" <fvbommel REMwOVExCAPSs.nl> wrote:
 Chris Miller wrote:
 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

parameters can have this attribute... Maybe it should be an attribute of the function instead? Then it becomes quite clear, I think: if there are multiple equally-good matches and among them the number of overloads with the attribute is anything other than one (that is, none or at least two) it's still an error, otherwise the one declared 'default' is picked.

So, we'd have something like default void foo(char[] s); void foo(wchar[] s); void foo(dchar[] s); with only one function of the same name at most with default...

I see no reason why you can't declare multiple 'default's, as long as you make sure they don't conflict. Basically, only one would be allowed in the overload set without it being an error. For instance: /// UTF conversion default char[] toString(char[]); char[] toString(wchar[]); /// ditto char[] toString(wchar[]); /// ditto /// Converts a number to a printable representation default char[] toString(int); char[] toString(uint); /// ditto I don't think these will conflict. Now, which one of the latter two to make the default, that's another question... (I just arbitrarily picked one)
 I like this :). Votes++ for this modification.

Dec 10 2006
parent Karen Lanrap <karen digitaldaemon.com> writes:
Frits van Bommel wrote:
 as long as you make sure they don't conflict.

How to make that sure in a sufficiently large coder team? How to find in case of conflicting defaulted overloads the right one in a long chain of derivings. How to find the usages of such defaulted overloads if there is a need to change the default, for example when a library from a different vendor must be used?
Dec 10 2006
prev sibling parent reply "Chris Miller" <chris dprogramming.com> writes:
On Sun, 10 Dec 2006 11:58:35 -0500, Frits van Bommel  
<fvbommel REMwOVExCAPSs.nl> wrote:

 Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
  Currently, if you try to do foo("hello"), the compiler will complain  
 because string literals simulteneous match all the above types.
  Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

 The compiler would go down the line of parameters of overloads, and  
 upon any conflicts, would look for default to resolve them. If default  
 is used to resolve conflicts in more that one of the functions, it's an  
 actual conflict.
  Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

I like the basic idea, but maybe it gets a bit to hairy when multiple parameters can have this attribute... Maybe it should be an attribute of the function instead? Then it becomes quite clear, I think: if there are multiple equally-good matches and among them the number of overloads with the attribute is anything other than one (that is, none or at least two) it's still an error, otherwise the one declared 'default' is picked.

I thought of it and generally agree it reduces complexity, but doesn't give as much control. Consider one name that has overloads with many different types, like toString; it has conflicts with numbers, strings, and whatever else. Also consider when more than one parameter is involved. I'm not sure which is the better tradeoff.
Dec 10 2006
parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Chris Miller wrote:
 On Sun, 10 Dec 2006 11:58:35 -0500, Frits van Bommel 
 <fvbommel REMwOVExCAPSs.nl> wrote:
 Maybe it should be an attribute of the function instead?
 Then it becomes quite clear, I think: if there are multiple 
 equally-good matches and among them the number of overloads with the 
 attribute is anything other than one (that is, none or at least two) 
 it's still an error, otherwise the one declared 'default' is picked.

I thought of it and generally agree it reduces complexity, but doesn't give as much control. Consider one name that has overloads with many different types, like toString; it has conflicts with numbers, strings, and whatever else. Also consider when more than one parameter is involved. I'm not sure which is the better tradeoff.

My version allows numbers and strings, as long as only one 'default' is in the overload set. Numbers and strings will never conflict, AFAIK. When it comes to multiple parameters, what kind of use case did you have in mind? I can't think of any at the moment, but that may just be my lack of imagination playing up again :).
Dec 10 2006
prev sibling next sibling parent Alexander Panek <a.panek brainsware.org> writes:
As I've already mentioned in #d, I /actually/ like it how it works now 
(explicit usage; e.g. you have to append c/w/d each time you pass a 
string literal). This concept is still the way to go, IMHO, though as D 
is a language inventing/implementing so much nice paradigms already, 
this one should have a go, too.

It's basically syntactic sugar, but also adds the capability to 
'control' usage of functions, which is a good thing.

Long story short:
Has my vote. :P

Kind regards,
Alex

Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
 
 Currently, if you try to do foo("hello"), the compiler will complain 
 because string literals simulteneous match all the above types.
 
 Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
 
 foo("hello")  will now select the char[] version because it was marked 
 as the default type in the overload.
 
 What it does not do:
    1) Resolve conflicts between different scopes.
    2) Override exact matches.
 
 The way overloads work now is helpful in some cases, but in other cases, 
 it's perfectly fine to prefer an overload over another.
 
 
 Example where you do not want a default overload: working with binary, 
 e.g. writing to Stream or Socket, where the wrong type can screw up the 
 format or transmission.
 
 Example where you do: custom string object's constructor that wants to 
 allow char[], wchar[] and dchar[] types, but wants to default string 
 literals to char[].
 
 
 The compiler would go down the line of parameters of overloads, and upon 
 any conflicts, would look for default to resolve them. If default is 
 used to resolve conflicts in more that one of the functions, it's an 
 actual conflict.
 
 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);
 
 Note: this example probably wouldn't actually be used; as with most 
 language constructs, there's a way to abuse it, but this example gets a 
 point across that it at least has logic:
 bar("hello", 42)  chooses the first one because the string literal 
 conflicted and went with the default one.
 bar("hello"c, 42)  chooses the second one; the string literal specified 
 char[] (with the c) and then had a conflict with the second parameter, 
 where default resolved it.
 
 
 The programmer knows which overloads, if any, can be preferred over 
 others, because this feature works only confined within one scope, and 
 so it is safe to let him choose for you.
 
 
 Where this can be useful:
    1) String literals conflicting with char[], wchar[] and dchar[].
    2) null conflicting with string types, delegates, pointers, and class 
 references.
    3) Integer literals conflicting with the number types.
    4) Different levels of derived classes conflicting with each other.
 
 
 Thanks,
 - Chris

Dec 10 2006
prev sibling parent reply Chris Nicholson-Sauls <ibisbasenji gmail.com> writes:
Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
 
 Currently, if you try to do foo("hello"), the compiler will complain 
 because string literals simulteneous match all the above types.
 
 Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);
 
 foo("hello")  will now select the char[] version because it was marked 
 as the default type in the overload.
 
 What it does not do:
    1) Resolve conflicts between different scopes.
    2) Override exact matches.
 
 The way overloads work now is helpful in some cases, but in other cases, 
 it's perfectly fine to prefer an overload over another.
 
 
 Example where you do not want a default overload: working with binary, 
 e.g. writing to Stream or Socket, where the wrong type can screw up the 
 format or transmission.
 
 Example where you do: custom string object's constructor that wants to 
 allow char[], wchar[] and dchar[] types, but wants to default string 
 literals to char[].
 
 
 The compiler would go down the line of parameters of overloads, and upon 
 any conflicts, would look for default to resolve them. If default is 
 used to resolve conflicts in more that one of the functions, it's an 
 actual conflict.
 
 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);
 
 Note: this example probably wouldn't actually be used; as with most 
 language constructs, there's a way to abuse it, but this example gets a 
 point across that it at least has logic:
 bar("hello", 42)  chooses the first one because the string literal 
 conflicted and went with the default one.
 bar("hello"c, 42)  chooses the second one; the string literal specified 
 char[] (with the c) and then had a conflict with the second parameter, 
 where default resolved it.
 
 
 The programmer knows which overloads, if any, can be preferred over 
 others, because this feature works only confined within one scope, and 
 so it is safe to let him choose for you.
 
 
 Where this can be useful:
    1) String literals conflicting with char[], wchar[] and dchar[].
    2) null conflicting with string types, delegates, pointers, and class 
 references.
    3) Integer literals conflicting with the number types.
    4) Different levels of derived classes conflicting with each other.
 
 
 Thanks,
 - Chris

Personally I just don't think I'd ever have a use for it... Rare is the string literal in my code without a c|w|d suffix. (Partially because I use Mango an awful lot, which does have a few cases of this ambiguity -- Stdout anyone? But no problem... I never code Stdout("Hello!") but always Stdout("Hello!"c) instead... tada, problem solved with existing features. But apparently a lot of people do see something useful about this. So I abstain and vote neutral. :) -- Chris Nicholson-Sauls
Dec 11 2006
parent reply Pragma <ericanderton yahoo.removeme.com> writes:
Chris Nicholson-Sauls wrote:
 Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

 Currently, if you try to do foo("hello"), the compiler will complain 
 because string literals simulteneous match all the above types.

 Proposal:
    void foo(default char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

 foo("hello")  will now select the char[] version because it was marked 
 as the default type in the overload.

 What it does not do:
    1) Resolve conflicts between different scopes.
    2) Override exact matches.

 The way overloads work now is helpful in some cases, but in other 
 cases, it's perfectly fine to prefer an overload over another.


 Example where you do not want a default overload: working with binary, 
 e.g. writing to Stream or Socket, where the wrong type can screw up 
 the format or transmission.

 Example where you do: custom string object's constructor that wants to 
 allow char[], wchar[] and dchar[] types, but wants to default string 
 literals to char[].


 The compiler would go down the line of parameters of overloads, and 
 upon any conflicts, would look for default to resolve them. If default 
 is used to resolve conflicts in more that one of the functions, it's 
 an actual conflict.

 Strange example:
    void bar(default char[] s, int x);
    void bar(char[] s, default long x);

 Note: this example probably wouldn't actually be used; as with most 
 language constructs, there's a way to abuse it, but this example gets 
 a point across that it at least has logic:
 bar("hello", 42)  chooses the first one because the string literal 
 conflicted and went with the default one.
 bar("hello"c, 42)  chooses the second one; the string literal 
 specified char[] (with the c) and then had a conflict with the second 
 parameter, where default resolved it.


 The programmer knows which overloads, if any, can be preferred over 
 others, because this feature works only confined within one scope, and 
 so it is safe to let him choose for you.


 Where this can be useful:
    1) String literals conflicting with char[], wchar[] and dchar[].
    2) null conflicting with string types, delegates, pointers, and 
 class references.
    3) Integer literals conflicting with the number types.
    4) Different levels of derived classes conflicting with each other.


 Thanks,
 - Chris

Personally I just don't think I'd ever have a use for it... Rare is the string literal in my code without a c|w|d suffix. (Partially because I use Mango an awful lot, which does have a few cases of this ambiguity -- Stdout anyone? But no problem... I never code Stdout("Hello!") but always Stdout("Hello!"c) instead... tada, problem solved with existing features. But apparently a lot of people do see something useful about this. So I abstain and vote neutral. :) -- Chris Nicholson-Sauls

The Mango example is actually a perfect case for how this can trip programmers up. But you're right, using existing features (as minimally intrusive as c/d/w) are the right way to go. Still, I've often wondered if there wasn't a better way. Something like auto defining char data as UTF-8/16/32 based on the *source file encoding* might go a long way to make this a little more implicit. However, I'm not sure if that would be too subtle to maintain practically. -- - EricAnderton at yahoo
Dec 11 2006
parent Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 11 Dec 2006 16:07:39 -0500, Pragma wrote:

 Chris Miller wrote:
 Given the following function prototypes:
    void foo(char[] s);
    void foo(wchar[] s);
    void foo(dchar[] s);

 Currently, if you try to do foo("hello"), the compiler will complain 
 because string literals simulteneous match all the above types.



...
 Still, I've often wondered if there wasn't a better way.

Would it be possible for the compiler to encode an unqualified string literal based on a hierarchy of matches. In other words, if there exists a char[] match use that, otherwise if there exists a wchar[] match then use that, otherwise if there exists a dchar[] match use that else there is no match. In Chris' original example the 'foo(char[])' would match and so the compiler encodes the literal as utf-8. Thus is multiple matches are possible, the compiler chooses them in order of utf-8, utf-16, and utf-32. Thus if the coder explicitly wants a specific 'foo' to be called they would need to explicitly encode the literal with the c/w/d qualifier. This idea seems reasonable and maybe also can be extended in concept to unqualified numeric literals. -- Derek (skype: derek.j.parnell) Melbourne, Australia "Down with mediocrity!" 12/12/2006 9:19:36 AM
Dec 11 2006