Issue with forward ranges which are reference types

Mehrdad wfunction at hotmail.com
Wed Aug 17 05:01:09 PDT 2011


On 8/16/2011 11:41 PM, Jonathan M Davis wrote:
> On Tuesday, August 16, 2011 23:20:14 Mehrdad wrote:
>> Right, but the problem is that none of this template business (e.g.
>> isInputRange!T, hasLength!T, etc.) works if the input is an Object that
>> implements InputRange.
>>
>> For example, consider this:
>>
>>       static Object getItems()
>>       { return inputRangeObject([1, 2]); }
>>
>>       Object collection = getItems();
>>       if (collection.empty)  //Whoops...
>>       {
>>           ...
>>       }
>>
>> The caller has no idea what kind of range is returned by getItems(), but
>> he still needs to be able to check whether it's empty.
>>
>> How can he figure this out? He would be forced to cast (which is by
>> itself a pretty bad option), but what can he cast the object to?
>> InputRange!Object doesn't work because it could be an InputRange!string
>> or something. There's really NO way (that I know of) for the caller to
>> test and see if the collection is an input range, unless he knows the
>> type -- which runs counter to what I've seen in other OOP languages (C#,
>> Java).
>>
>> Hope that makes sense...
> If you're dealing with a reference for a type that implements the functions
> for input range or forward range or whatever, then it's not an issue. It'll
> work with functions that require those range types. If you're dealing with a
> reference that doesn't implement the appropriate functions, then it isn't even
> if the actual type does. You could cast to the actual type and use that, but
> that pretty much assumes that you know the actual type - or if you don't you
> end up having to do something like
>
> auto ir = cast(InputRange)obj;
That doesn't compile. I think you missed the entire point of my comment 
-- you have no idea what it's an input range OF.
Read below.
> if(ir)
>      //call range func
> else
>     //do whatever you do if you can't call the range func
>
> However, that OO _normally_ works is that you use a reference which is the
> type that you want to treat the object as. So, all of the code using that
> reference only deals with functionality that that reference type has. You
> don't usually cast it to other types to try and do other stuff. So, if a
> function needs the InputRange class/interface, then that's the reference that
> you use for that particular variable, and then whatever you assign it to (from
> a function parameter or a return function or whatever) is a type which derives
> from or implements InputRange. And it all works just fine.
>
> It's usually _bad_ OO to be casting between object types. In particular,
> actually using the base Object class is usually a _bad_ idea. Some languages
> do that for their containers because they lack proper templates, but then you
> have to worry about casting the objects to the correct type when you get them
> out. It can work, but it's not a great idea. Java and C# used be like that,
> but when they added generics, they made it so that only the internal
> implementation works that way. The generics take care of keeping track of the
> actual type of the objects in the container and you don't have to cast anymore
> (though the casts still occur underneath the hood). So, that improves the
> situation considerably.
>
> But regardless, good OO design does not usually require casting from a base
> class or interface to a derived class. So, the issue that you're describing
> just doesn't happen in good OO code.
>
> - Jonathan M Davis
I think you missed my point.

My point wasn't "What if all you have is an Object reference?", but 
rather "What if you don't know the _kind_ of InputRange(T) an object is?".

i.e. You might know very well that a piece of code returns an 
InputRange(T) where T is _SOME_ subclass of a class you know, but not 
have any idea what T is.

When does that happen? When you're dealing with covariance and 
contravariance. You get back a container of SOME kind of object 
reference, but you don't know what kind of container it is, so there's 
NO way for you to "just cast it" to InputRange(T) because you don't know 
what T would be.

"Proper" OOP languages (e.g. Java and C#) have support for that 
situation by letting you return InputRange!T where T is some BASE class 
of what you have. But you can't do that in D (...AFAIK?) so you're 
forced to return an Object, hence my example.

Of course, your argument is that we need to use The Template Hammer, and 
make the CALLER be a template, so that it can accept anything. The 
problem with which is that now your template leaks, i.e. now you're 
forcing a whole bunch of other code to become templated, when it really  
doesn't need to be. You consider that a good idea, but I think you're 
completely ignoring the fact that the entire concept of a shared library 
is to SHARE code. Once you "templatize" a piece of code and then force 
everything else to follow suit, then you can't share the same code -- 
it's NEW code EVERY time. So unless you also consider shared libraries 
to be an indicator of Bad Coding ("only n00bs don't know EVERYTHING at 
compile time") I'm just confused at how you can think that The Template 
Hammer should be used for every nail. It /just fails/ when you're making 
a shared library.


More information about the Digitalmars-d mailing list