Bug or Feature? compile error: to!string(const Object)

Jonathan M Davis via Digitalmars-d digitalmars-d at puremagic.com
Wed Jul 2 10:21:17 PDT 2014


On Wed, 02 Jul 2014 11:33:54 +0000
Wanderer via Digitalmars-d <digitalmars-d at puremagic.com> wrote:

> "~" operator has to do with toString(), because it performs
> string concatenation (at least documentation says so) and
> toString() is the common way to convert arbitrary object into a
> string.

The common way to convert anything to string in D, is to use std.conv.to -
i.e. to!string(foo). It's generic and works not only with anything that
defines toString but with built-in types as well. Most code should not be
calling toString directly.

> Having ~ operator implemented the way you explained - via
> opBinary!"~" in each class and not in Object class - looks like a
> bad idea to me, because 1) the same concatenation code will be
> duplicated across various classes, including user-defined ones,
> and 2) user-defined classes can define "~" in some uncommon way
> which would confuse everyone who uses these classes. Java's
> approach (one common implementation of the "+" operator for
> strings concatenation) only requires user classes to define
> toString() method without redefining string operations, which is
> convenient.

If you want to be able to concatenate two objects, they must define
opBinary!"~". That's the case now. If you want to convert two objects to
strings and concatenate those, then you convert them to strings and
concatenate them, e.g. to!string(foo) ~ to!string(bar) or
format("%s%s", foo, bar). Implementing toString does not automatically make
anything concatenatable except for the strings that you get when converting
those objects to strings. The objects themselves are not concatenatable.

> "Languages like Java and C# are forced to, because they do not
> have proper templates and thus have to use Object directly in
> many cases."
>
> That's not exactly true. The main reason why Object class has
> these methods, is the ability to define general contracts for
> these methods. For example, Object defines once and for all that
> toString() returns String representing object's contents, or that
> equals() and hashCode() results comply with each other. If you
> remove these methods from Object, you will dispense these
> contracts as well, forcing programmers to implement these methods
> in least predictable way. That will cause chaos.

There's nothing chaotic about it. It's how templates work, and a lot of D uses
them already - e.g. that's how the range API is defined. Template constraints
do a wonderful job of ensuring that templated functions are called on types
which implement the functions and properties with the right API, and all four
of these functions which are on Object are already being defined by
programmers without any kind of base class for structs. In addition to that,
_none_ of these functions should be called by most programs. toString should
be called by to!string, toHash is normally only needed by the built-in AAs or
other hash table implementations.  opEquals is called by using == or !=, and
opCmp is used by using any of the other comparison operators. _Some_ code may
have to use one or more of those four functions directly, but it should be
extremely rare. The average program shouldn't need to care whether Object has
any of these functions.

By not putting these functions on Object, it allows them to have whatever
attributes they need when declared in derived types. Without that, we're stuck
with whatever attributes are on the one in Object, which is unacceptably
restrictive. The _only_ thing we lose here is the ability to call any of these
functions on Object directly, which arguably is no real loss.

- Jonathan M Davis


More information about the Digitalmars-d mailing list