December 04, 2012
On Tuesday, 4 December 2012 at 07:38:31 UTC, Maxim Fomin wrote:
> On Monday, 3 December 2012 at 23:53:26 UTC, deadalnix wrote:
>> On Monday, 3 December 2012 at 21:53:47 UTC, Walter Bright wrote:
>>> I really don't know what issue you're trying to solve here. The typeid's work fine - and interfaces are not objects. Having the typeid for an interface be an object means you cannot compare typeids of one interface to another interface.
>>>
>>
>> You can't instantiate interface. So an underlying type MUST exist. The whole point of typeid on expression is to discover what is the dynamic type of things, otherwize you'd be using typeid(type) not typeid(expression).
>
> You cannot create interface instance with new operator because interface object is not valid until it is actually some class instance. But this does not mean neither that typeid operator for interfaces should return dynamic type nor that interface can be implicitly converted to Object - because interface instance may be invalid:
>
> import std.stdio;
>
> interface I { }
>
> void main()
> {
> 	I i;
>         // should be implicitly converted
> 	Object o = cast(Object)i;
> 	writeln(typeid(o));
> }
>
> and because presence of interface does not necessarily mean that some class has implemented it.
>
> This makes casting interfaces to object unsafe operation that better should require explicit cast.

Given my experience with the interface concept in several OO languages that implement it, I would say that you never want to know what object was given to you.

The whole point of interfaces is to have some form of multiple inheritance in single inheritance languages. So you *really* don't want to cast interfaces to objects.

I can't think of a single reason this makes sense, other than work around bad designs.

--
Paulo


December 04, 2012
On Monday, 3 December 2012 at 20:59:24 UTC, Walter Bright wrote:
> On 12/4/2012 2:52 AM, Jacob Carlborg wrote:
>> Note, I'm not saying that an interface should be implicitly converted to
>> any class, only to Object.
>
>
> But not all interfaces come from Objects.

IMO this is a design mistake - the special case is preferred over the common case which goes against the D philosophy of making the common case easy and the special case possible.

All COM instances are known at _compile_time_ as they're all required to inherit from a special IUnknown interface so the compiler _knows_ if indeed an interface represents a COM instance or not. This can be used to handle this special case differently.

Interface i = new Class();
Object o = i; // should work for regular interfaces

The compiler can issue a compile-time error if i is COM since this is known at compile type.
December 04, 2012
On Tuesday, 4 December 2012 at 07:38:31 UTC, Maxim Fomin wrote:
> On Monday, 3 December 2012 at 23:53:26 UTC, deadalnix wrote:
>> On Monday, 3 December 2012 at 21:53:47 UTC, Walter Bright wrote:
>>> I really don't know what issue you're trying to solve here. The typeid's work fine - and interfaces are not objects. Having the typeid for an interface be an object means you cannot compare typeids of one interface to another interface.
>>>
>>
>> You can't instantiate interface. So an underlying type MUST exist. The whole point of typeid on expression is to discover what is the dynamic type of things, otherwize you'd be using typeid(type) not typeid(expression).
>
> You cannot create interface instance with new operator because interface object is not valid until it is actually some class instance. But this does not mean neither that typeid operator for interfaces should return dynamic type nor that interface can be implicitly converted to Object - because interface instance may be invalid:
>
> import std.stdio;
>
> interface I { }
>
> void main()
> {
> 	I i;
>         // should be implicitly converted
> 	Object o = cast(Object)i;
> 	writeln(typeid(o));
> }
>
> and because presence of interface does not necessarily mean that some class has implemented it.
>
> This makes casting interfaces to object unsafe operation that better should require explicit cast.

The above is a perfectly safe conversion.
The variable i is _a reference_ to instance of I. since it was not assigned any class instance that implements I, it is initialized as _null_.
In the example above: (o is null && typeid(o) == typeid(Object))

December 04, 2012
On 2012-12-04 09:22, Maxim Fomin wrote:

> And what happens if nobody implements an interface?
>
> import std.stdio;
>
> interface I { }
>
> class A { }
>
> void main()
> {
>      I i;
>      // assume this is implicit
>      Object o = cast(Object)i;
>      writeln(typeid(o));
> }

You get a segmentation fault since both "i" and "o" are null.

> Safe conversion class to interface requires two conditions:
> 1a) that class implements interface
> 1b) if you try to use interface variable, it must be an allocated class
> instance
>
> Safe conversion to Object requires:
> 2a) somebody in class hierarchy implements interface
> 2b) interface instance is actually allocated class instance

You cannot really get an instance of an interface without having a class implementing it. That is, without inserting any explicit casts, which works:

interface I { }

class A { }

void main()
{
    A a = new A;
    I i = cast(I) a;
    Object o = cast(Object)i;
    writeln(typeid(a)); // A
}

> It is possible to check 1a) but impossible in general case to check 2a).
> Also the first is design feature while the second is design abuse.

I don't understand why it wouldn't be safe to allow implicit casts of interfaces to Object.

If I want to call toString, why should I need to insert an explicit cast to Object just because I have an interface?

-- 
/Jacob Carlborg
December 04, 2012
On 2012-12-04 13:15, foobar wrote:

> IMO this is a design mistake - the special case is preferred over the
> common case which goes against the D philosophy of making the common
> case easy and the special case possible.
>
> All COM instances are known at _compile_time_ as they're all required to
> inherit from a special IUnknown interface so the compiler _knows_ if
> indeed an interface represents a COM instance or not. This can be used
> to handle this special case differently.
>
> Interface i = new Class();
> Object o = i; // should work for regular interfaces
>
> The compiler can issue a compile-time error if i is COM since this is
> known at compile type.

Exactly, I couldn't agree more.

-- 
/Jacob Carlborg
December 04, 2012
On Tuesday, 4 December 2012 at 12:26:53 UTC, Jacob Carlborg wrote:
> On 2012-12-04 09:22, Maxim Fomin wrote:
>
>> And what happens if nobody implements an interface?
>>
>> import std.stdio;
>>
>> interface I { }
>>
>> class A { }
>>
>> void main()
>> {
>>     I i;
>>     // assume this is implicit
>>     Object o = cast(Object)i;
>>     writeln(typeid(o));
>> }
>
> You get a segmentation fault since both "i" and "o" are null.
>
>> Safe conversion class to interface requires two conditions:
>> 1a) that class implements interface
>> 1b) if you try to use interface variable, it must be an allocated class
>> instance
>>
>> Safe conversion to Object requires:
>> 2a) somebody in class hierarchy implements interface
>> 2b) interface instance is actually allocated class instance
>
> You cannot really get an instance of an interface without having a class implementing it. That is, without inserting any explicit casts, which works:
>
> interface I { }
>
> class A { }
>
> void main()
> {
>     A a = new A;
>     I i = cast(I) a;
>     Object o = cast(Object)i;
>     writeln(typeid(a)); // A
> }
>
>> It is possible to check 1a) but impossible in general case to check 2a).
>> Also the first is design feature while the second is design abuse.
>
> I don't understand why it wouldn't be safe to allow implicit casts of interfaces to Object.
>
> If I want to call toString, why should I need to insert an explicit cast to Object just because I have an interface?

If you want to call toSring, it should be part of the interface specification, as simple as that.

Interfaces are like contracts which state what is to be expected of a certain type.

--
Paulo

December 04, 2012
On Tuesday, 4 December 2012 at 12:30:29 UTC, Paulo Pinto wrote:
> On Tuesday, 4 December 2012 at 12:26:53 UTC, Jacob Carlborg wrote:
>> On 2012-12-04 09:22, Maxim Fomin wrote:
>>
>>> And what happens if nobody implements an interface?
>>>
>>> import std.stdio;
>>>
>>> interface I { }
>>>
>>> class A { }
>>>
>>> void main()
>>> {
>>>    I i;
>>>    // assume this is implicit
>>>    Object o = cast(Object)i;
>>>    writeln(typeid(o));
>>> }
>>
>> You get a segmentation fault since both "i" and "o" are null.
>>
>>> Safe conversion class to interface requires two conditions:
>>> 1a) that class implements interface
>>> 1b) if you try to use interface variable, it must be an allocated class
>>> instance
>>>
>>> Safe conversion to Object requires:
>>> 2a) somebody in class hierarchy implements interface
>>> 2b) interface instance is actually allocated class instance
>>
>> You cannot really get an instance of an interface without having a class implementing it. That is, without inserting any explicit casts, which works:
>>
>> interface I { }
>>
>> class A { }
>>
>> void main()
>> {
>>    A a = new A;
>>    I i = cast(I) a;
>>    Object o = cast(Object)i;
>>    writeln(typeid(a)); // A
>> }
>>
>>> It is possible to check 1a) but impossible in general case to check 2a).
>>> Also the first is design feature while the second is design abuse.
>>
>> I don't understand why it wouldn't be safe to allow implicit casts of interfaces to Object.
>>
>> If I want to call toString, why should I need to insert an explicit cast to Object just because I have an interface?
>
> If you want to call toSring, it should be part of the interface specification, as simple as that.
>
> Interfaces are like contracts which state what is to be expected of a certain type.
>
> --
> Paulo

Generally speaking you are right. But specifically regarding toString, what would you suggest?
Should each and every Interface have a toString method?
Should we have an IObject Interface that all classes are required to explicitly inherit?
The purpose of having a root Object class is to define the _common interface of all objects_. Why else have such a class in the first place?
Requiring an explicit cast to Object makes sense only in a no-single-root design such as the one in c++ which D sensibly chose not to copy.

We can have a separate debate what should the root Object define, but there should not be a requirement to explicitly cast any object to it.
December 04, 2012
On Tuesday, 4 December 2012 at 13:07:59 UTC, foobar wrote:
> On Tuesday, 4 December 2012 at 12:30:29 UTC, Paulo Pinto wrote:
>> On Tuesday, 4 December 2012 at 12:26:53 UTC, Jacob Carlborg wrote:
>>> On 2012-12-04 09:22, Maxim Fomin wrote:
>>>
>>>> And what happens if nobody implements an interface?
>>>>
>>>> import std.stdio;
>>>>
>>>> interface I { }
>>>>
>>>> class A { }
>>>>
>>>> void main()
>>>> {
>>>>   I i;
>>>>   // assume this is implicit
>>>>   Object o = cast(Object)i;
>>>>   writeln(typeid(o));
>>>> }
>>>
>>> You get a segmentation fault since both "i" and "o" are null.
>>>
>>>> Safe conversion class to interface requires two conditions:
>>>> 1a) that class implements interface
>>>> 1b) if you try to use interface variable, it must be an allocated class
>>>> instance
>>>>
>>>> Safe conversion to Object requires:
>>>> 2a) somebody in class hierarchy implements interface
>>>> 2b) interface instance is actually allocated class instance
>>>
>>> You cannot really get an instance of an interface without having a class implementing it. That is, without inserting any explicit casts, which works:
>>>
>>> interface I { }
>>>
>>> class A { }
>>>
>>> void main()
>>> {
>>>   A a = new A;
>>>   I i = cast(I) a;
>>>   Object o = cast(Object)i;
>>>   writeln(typeid(a)); // A
>>> }
>>>
>>>> It is possible to check 1a) but impossible in general case to check 2a).
>>>> Also the first is design feature while the second is design abuse.
>>>
>>> I don't understand why it wouldn't be safe to allow implicit casts of interfaces to Object.
>>>
>>> If I want to call toString, why should I need to insert an explicit cast to Object just because I have an interface?
>>
>> If you want to call toSring, it should be part of the interface specification, as simple as that.
>>
>> Interfaces are like contracts which state what is to be expected of a certain type.
>>
>> --
>> Paulo
>
> Generally speaking you are right. But specifically regarding toString, what would you suggest?
> Should each and every Interface have a toString method?

Yes for each interface where you intend to call toString().

> Should we have an IObject Interface that all classes are required to explicitly inherit?
> The purpose of having a root Object class is to define the _common interface of all objects_. Why else have such a class in the first place?

For languages without generics where you need a common base class to place in containers.

> Requiring an explicit cast to Object makes sense only in a no-single-root design such as the one in c++ which D sensibly chose not to copy.
>
> We can have a separate debate what should the root Object define, but there should not be a requirement to explicitly cast any object to it.

The whole point of interfaces is to have explicit dependencies of
methods, properties, variables across the inheritance tree.

Actually there was a discussion some months ago about what methods still
made sense to expose via object, given D's templates.

--
Paulo

December 04, 2012
On Tuesday, 4 December 2012 at 13:47:39 UTC, Paulo Pinto wrote:

>> Generally speaking you are right. But specifically regarding toString, what would you suggest?
>> Should each and every Interface have a toString method?
>
> Yes for each interface where you intend to call toString().

That's a lot of duplication considering D already provides this in Object.

>
>> Should we have an IObject Interface that all classes are required to explicitly inherit?
>> The purpose of having a root Object class is to define the _common interface of all objects_. Why else have such a class in the first place?
>
> For languages without generics where you need a common base class to place in containers.

That's only one reason. Other reasons are to provide a common interface for all objects. Anyway, we are discussing the current D design and not other possible designs such as the one in C++.

>
>> Requiring an explicit cast to Object makes sense only in a no-single-root design such as the one in c++ which D sensibly chose not to copy.
>>
>> We can have a separate debate what should the root Object define, but there should not be a requirement to explicitly cast any object to it.
>
> The whole point of interfaces is to have explicit dependencies of
> methods, properties, variables across the inheritance tree.
>
> Actually there was a discussion some months ago about what methods still
> made sense to expose via object, given D's templates.
>
> --
> Paulo

Given D's _current_ design, all objects should implicitly cast to Object.
It's plain common sense - If we have a root class that all other classes inherit from than it logically follows that all class instances can be implicitly & safely converted back to that root class (Object).

What you are arguing is whether the current design of D of defining Object makes sense in the first place. I'm arguing that _given the current design_ the language is inconsistent and has a design flaw.
December 04, 2012
On Tuesday, December 04, 2012 17:35:23 foobar wrote:
> That's a lot of duplication considering D already provides this in Object.

Though per the last major discussion on const-correctness and Object, it's likely that toString, toHash, opCmp, and opEquals will be removed from Object, in which case you'd need a derived class which implemented them to use any of them.

- Jonathan m Davis