www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - toString ugliness

reply Jerry <jlquinn optonline.net> writes:
toString() doesn't work inside a class member function.

import std.string;
class A {
  void f() {
    string s = toString(5);
  }
}

This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

This is a nuisance and a wart (though not a bug per-se).

If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.

It can be solved somewhat by documenting clearly that to!(string)(int)
be used instead, which seems silly to me.  I'm irritated by the 3 extra chars
required to type a to!(type) template.

.toString() works around the problem, but why should it be needed?  This is
unfortunate.

Does this bother anyone else?  If not, I'll return to my lurking cave :-)
Dec 06 2008
next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.

 It can be solved somewhat by documenting clearly that to!(string)(int)
 be used instead, which seems silly to me.  I'm irritated by the 3 extra chars
required to type a to!(type) template.

 .toString() works around the problem, but why should it be needed?  This is
unfortunate.

 Does this bother anyone else?  If not, I'll return to my lurking cave :-)

It's annoying but it's not enough to make me want to change anything. ;)
Dec 06 2008
parent reply Jerry <jlquinn optonline.net> writes:
Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


As I'm thinking about this, why does this actually fail? The parameter doesn't match the prototype for this.toString(), so why not match against the global function? Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?
Dec 06 2008
next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Jarrett Billingsley escribió:
 On Sat, Dec 6, 2008 at 12:11 PM, Jerry <jlquinn optonline.net> wrote:
 Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?

As far as I know, it's because D does not use ADL (argument-dependent lookup). That is, it simply looks for the toString that is in the nearest enclosing scope, and if it doesn't match - too bad. Compare this behavior to C++, where it would then continue on, looking for a definition of toString that does match. Most of the time ADL leads to confusing behavior, which is why it was dropped in D.

But the overloading is obvious! It looks for toString(...) and it founds it in the class, but the overloading is wrong. So for me, it should keep looking in the enclosing scopes.
Dec 07 2008
parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Jarrett Billingsley escribió:
 On Sun, Dec 7, 2008 at 7:12 AM, Ary Borenszweig <ary esperanto.org.ar> wrote:
 definition of toString that does match.  Most of the time ADL leads to
 confusing behavior, which is why it was dropped in D.

in the class, but the overloading is wrong. So for me, it should keep looking in the enclosing scopes.

That's why I said "*most* of the time" ;) I'll agree that it's a bit counterintuitive, but I'd rather have the compiler be a little stupid in this regard than to pay the price of unexpected function matches for a little bit of convenience.

Ah, I see. Well, I'd like to see such an example then. :-P
Dec 07 2008
parent reply Christopher Wright <dhasenan gmail.com> writes:
Ary Borenszweig wrote:
 Jarrett Billingsley escribió:
 On Sun, Dec 7, 2008 at 7:12 AM, Ary Borenszweig <ary esperanto.org.ar> 
 wrote:
 definition of toString that does match.  Most of the time ADL leads to
 confusing behavior, which is why it was dropped in D.

founds it in the class, but the overloading is wrong. So for me, it should keep looking in the enclosing scopes.

That's why I said "*most* of the time" ;) I'll agree that it's a bit counterintuitive, but I'd rather have the compiler be a little stupid in this regard than to pay the price of unexpected function matches for a little bit of convenience.

Ah, I see. Well, I'd like to see such an example then. :-P

It usually won't be an issue, I think. You'll give your functions descriptive names, and you won't have overloads in different scopes that take similar arguments. So any example I give will probably seem contrived. I certainly have only encountered this compiler error a bare handful of times, and only with toString. That said, errors of this kind would be difficult to track down, and the workaround is very simple once you know it -- and not that difficult to come up with if you don't. I think I came up using .toString without having anyone tell me about it, and if I hadn't, I would have used the fully qualified name (or an alias).
Dec 07 2008
parent reply "Nick Sabalausky" <a a.a> writes:
"Christopher Wright" <dhasenan gmail.com> wrote in message 
news:ghhbn3$2u32$1 digitalmars.com...
 Ary Borenszweig wrote:
 Jarrett Billingsley escribió:
 On Sun, Dec 7, 2008 at 7:12 AM, Ary Borenszweig <ary esperanto.org.ar> 
 wrote:
 definition of toString that does match.  Most of the time ADL leads to
 confusing behavior, which is why it was dropped in D.

founds it in the class, but the overloading is wrong. So for me, it should keep looking in the enclosing scopes.

That's why I said "*most* of the time" ;) I'll agree that it's a bit counterintuitive, but I'd rather have the compiler be a little stupid in this regard than to pay the price of unexpected function matches for a little bit of convenience.

Ah, I see. Well, I'd like to see such an example then. :-P

It usually won't be an issue, I think. You'll give your functions descriptive names, and you won't have overloads in different scopes that take similar arguments. So any example I give will probably seem contrived. I certainly have only encountered this compiler error a bare handful of times, and only with toString. That said, errors of this kind would be difficult to track down, and the workaround is very simple once you know it -- and not that difficult to come up with if you don't. I think I came up using .toString without having anyone tell me about it, and if I hadn't, I would have used the fully qualified name (or an alias).

I kind of like Ruby's requirement that accessing "this.whatever" *must* be done like " whatever" (with the "this." part not being needed). Omitting the means you're talking about either a local identifier or a global one. I don't know if that stuff applies to member functions as well or if it's just member vars, but if it did apply to member funcs then that would certainly solve the "finding the correct toString" issue. Of course, that would probably be way too big of a syntax change for D, though.
Dec 07 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky:
 I kind of like Ruby's requirement that accessing "this.whatever" *must* be 
 done like " whatever" (with the "this." part not being needed). Omitting the 
   means you're talking about either a local identifier or a global one. I 
 don't know if that stuff applies to member functions as well or if it's just 
 member vars, but if it did apply to member funcs then that would certainly 
 solve the "finding the correct toString" issue. Of course, that would 
 probably be way too big of a syntax change for D, though.

In Ruby you also use for class variables (similar to static attributes of classes in D). I have often seen C++ code where the name of instance attributes are prefixed by "m_". In D now and then I use "this.somename" to avoid name clashes (for example with argument variables) or to make more explicit that a variable isn't local. Using a default prefix, like or $ to instance attributes in D2 code will make such code look a little ugly, but probably also more explicit too. (It's also short, shorter than "m_" and shorter than "this."). So I may end liking this syntax change. Bye, bearophile
Dec 07 2008
prev sibling parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sat, 6 Dec 2008 13:04:56 -0500, Jarrett Billingsley wrote:

 On Sat, Dec 6, 2008 at 12:11 PM, Jerry <jlquinn optonline.net> wrote:
 Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


As I'm thinking about this, why does this actually fail? The parameter doesn't match the prototype for this.toString(), so why not match against the global function? Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?

As far as I know, it's because D does not use ADL (argument-dependent lookup). That is, it simply looks for the toString that is in the nearest enclosing scope, and if it doesn't match - too bad. Compare this behavior to C++, where it would then continue on, looking for a definition of toString that does match.

Not correct. C++ uses the same rules to build overload sets as D. That is, a function declaration in an inner scope hides any function declarations with the same name in outer scopes. I /think/ there should be a good rationale behind this design. Actually, any declaration works like this. A variable declaration hides any declarations with the same name in the outer scopes. Probably functions follow this rule to make overloading more predictable.
Dec 07 2008
parent Christopher Wright <dhasenan gmail.com> writes:
Sergey Gromov wrote:
 Not correct.  C++ uses the same rules to build overload sets as D.  That
 is, a function declaration in an inner scope hides any function
 declarations with the same name in outer scopes.  I /think/ there should
 be a good rationale behind this design.

I've been using C++ lately, and I'd put it a bit differently: I think there /should/ be a good rationale behind its design.
Dec 07 2008
prev sibling next sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sat, Dec 6, 2008 at 12:11 PM, Jerry <jlquinn optonline.net> wrote:
 Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


As I'm thinking about this, why does this actually fail? The parameter doesn't match the prototype for this.toString(), so why not match against the global function? Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?

As far as I know, it's because D does not use ADL (argument-dependent lookup). That is, it simply looks for the toString that is in the nearest enclosing scope, and if it doesn't match - too bad. Compare this behavior to C++, where it would then continue on, looking for a definition of toString that does match. Most of the time ADL leads to confusing behavior, which is why it was dropped in D.
Dec 06 2008
prev sibling next sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sat, Dec 6, 2008 at 12:11 PM, Jerry <jlquinn optonline.net> wrote:
 Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


As I'm thinking about this, why does this actually fail? The parameter doesn't match the prototype for this.toString(), so why not match against the global function? Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?

As far as I know, it's because D does not use ADL (argument-dependent lookup). That is, it simply looks for the toString that is in the nearest enclosing scope, and if it doesn't match - too bad. Compare this behavior to C++, where it would then continue on, looking for a definition of toString that does match. Most of the time ADL leads to confusing behavior, which is why it was dropped in D.
Dec 06 2008
prev sibling next sibling parent Steve Schveighoffer <schveiguy yahoo.com> writes:
On Sat, 06 Dec 2008 11:21:11 -0500, Jerry wrote:

 toString() doesn't work inside a class member function.
 
 import std.string;
 class A {
   void f() {
     string s = toString(5);
   }
 }
 
 This errors with junk.d(19): function object.Object.toString () does not
 match parameter types (int)
 
 This is a nuisance and a wart (though not a bug per-se).
 
 If the language really can't handle distinguishing this.toString() from
 toString(int), then std.string.toString really should have a different
 name.
 
 It can be solved somewhat by documenting clearly that to!(string)(int)
 be used instead, which seems silly to me.  I'm irritated by the 3 extra
 chars required to type a to!(type) template.
 
 .toString() works around the problem, but why should it be needed?  This
 is unfortunate.
 
 Does this bother anyone else?  If not, I'll return to my lurking cave
 :-)

Doesn't bother me at all. Using tango, I usually rename the appropriate toString module imports. i.e.: import Int = tango.text.convert.Integer; Int.toString(5); But you'd probably hate that. That's *4* more characters! Or you could alias it at the top of the module you want to use it in. alias toString toStr; The object.toString function is so well known, and so intuitive, I doubt there would be any possible way it would be changed. Nor would I want that to change. -Steve
Dec 06 2008
prev sibling next sibling parent reply Ary Borenszweig <ary esperanto.org.ar> writes:
Jerry escribió:
 toString() doesn't work inside a class member function.
 
 import std.string;
 class A {
   void f() {
     string s = toString(5);
   }
 }
 
 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)
 
 This is a nuisance and a wart (though not a bug per-se).
 
 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.
 
 It can be solved somewhat by documenting clearly that to!(string)(int)
 be used instead, which seems silly to me.  I'm irritated by the 3 extra chars
required to type a to!(type) template.
 
 .toString() works around the problem, but why should it be needed?  This is
unfortunate.
 
 Does this bother anyone else?  If not, I'll return to my lurking cave :-)

It bothers me. Maybe it doesn't bother anyone else in this newsgroup because they already fell in that trap and they know the solution or the workarounds. For me, there shouldn't be a workaround, it should just work. If I, a human, can understand that toString(int) can't never, EVER, be confused with toString(), why can't the compiler see the same?
Dec 07 2008
parent bearophile <bearophileHUGS lycos.com> writes:
Ary Borenszweig:
 It bothers me. Maybe it doesn't bother anyone else in this newsgroup 
 because they already fell in that trap and they know the solution or the 
 workarounds.

I too have fallen in this little trap, but after the first time you learn to add a "." before toString. I use str()/repr() functions from my dlibs to avoid name clashes with toString() (see below), so I think that it may be better for the method and the global function to have different names. Python avoids such problem because the function and the method have different names: the global built-in functions are named str() and repr() and the methods are __str__() and __repr__(). (each function calls the relative method. If __str__ is absent, __repr__ is called as fallback). str() gives a human-readable textual representation of something, and repr() gives a string representation that whenever possible is the text you have to write to define that thing in the code, so generally eval(repr(x)) == x. Bye, bearophile
Dec 07 2008
prev sibling next sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sun, Dec 7, 2008 at 7:12 AM, Ary Borenszweig <ary esperanto.org.ar> wrote:
 definition of toString that does match.  Most of the time ADL leads to
 confusing behavior, which is why it was dropped in D.

But the overloading is obvious! It looks for toString(...) and it founds it in the class, but the overloading is wrong. So for me, it should keep looking in the enclosing scopes.

That's why I said "*most* of the time" ;) I'll agree that it's a bit counterintuitive, but I'd rather have the compiler be a little stupid in this regard than to pay the price of unexpected function matches for a little bit of convenience.
Dec 07 2008
prev sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Sun, Dec 7, 2008 at 5:54 PM, Sergey Gromov <snake.scaly gmail.com> wrote:
 Sat, 6 Dec 2008 13:04:56 -0500, Jarrett Billingsley wrote:

 On Sat, Dec 6, 2008 at 12:11 PM, Jerry <jlquinn optonline.net> wrote:
 Jarrett Billingsley Wrote:

 On Sat, Dec 6, 2008 at 11:21 AM, Jerry <jlquinn optonline.net> wrote:
 toString() doesn't work inside a class member function.

 import std.string;
 class A {
  void f() {
    string s = toString(5);
  }
 }

 This errors with junk.d(19): function object.Object.toString () does not match
parameter types (int)

 This is a nuisance and a wart (though not a bug per-se).

 If the language really can't handle distinguishing this.toString() from
toString(int), then std.string.toString really should have a different name.


As I'm thinking about this, why does this actually fail? The parameter doesn't match the prototype for this.toString(), so why not match against the global function? Is this due to the behavior that override solves? Would marking Object.toString() as override fix the problem?

As far as I know, it's because D does not use ADL (argument-dependent lookup). That is, it simply looks for the toString that is in the nearest enclosing scope, and if it doesn't match - too bad. Compare this behavior to C++, where it would then continue on, looking for a definition of toString that does match.

Not correct. C++ uses the same rules to build overload sets as D. That is, a function declaration in an inner scope hides any function declarations with the same name in outer scopes. I /think/ there should be a good rationale behind this design. Actually, any declaration works like this. A variable declaration hides any declarations with the same name in the outer scopes. Probably functions follow this rule to make overloading more predictable.

Sorry.
Dec 07 2008