Reputation: 19339
My question is pretty much what the title says: Is it possible to have a programming language which does not allow explicit type casting?
To clarify what I mean, assume we're working in some C#-like language with a parent Base
class and a child Derived
class. Clearly, such code would be safe:
Base a = new Derived();
Since going up the inheritance hierarchy is safe, but
Dervied b = (Base)a;
is not guarenteed safe, since going down is not safe.
But, regardless of the safety, such downcasts are valid in many languages (like Java or C#) - the code will compile, and will simply fail at runtime if the types aren't right. So technically, the code is still safe, but via runtime checks and not compile-time checks (btw, I'm not a fan of runtime checks).
I would personally find complete compile-time type safety to be very important, at least from a theoretical perspective, and at most from the perspective of reliable code. A consequence of compile-time type safety is that casts are no longer needed (which I think is great, 'cause they're ugly anyways). Any cast-like behaviour can be implemented by an implicit conversion operator or by a constructor.
So I'm wondering, are currently any OO languages which provide such a rigourous type safety at compile-time that casts are obsolete? I.e., they don't any allow unsafe conversion operations whatsoever? Or is there a reason this wouldn't work?
Thanks for any input.
If I can clarify by example, here's the big reason I hate downcasts so much.
Let's say I have the following (loosely based on C#'s collections):
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
IEnumerable<T> Filter( Func<T, bool> );
}
public class List<T> : IEnumerable<T>
{
// All of list's implementation here
}
Now suppose someone decides to write code like this:
List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Let's filter out the odd numbers
List<int> result = (List<int>)list.Filter( x => x % 2 != 0 );
Notice how the cast is necessary on that last line. But is it valid? Not in general. Sure, it makes sense that the implementation of List<T>.Filter
will return another List<T>
, but this is not guarenteed (it could be any subtype of IEnumerable<T>
). Even if this runs at one point in time, a later version may change this, exposing how brittle the code is.
Pretty much all of the situations I can think that require downcasts would boil down to something like this example - a method has a return type of some class or interface, but since we know some implementation details, we're confident in downcasting the result. But this is anti-OOP, since OOP actually encourages abstracting from implementation details. So why do we do it anyways, even in purely OOP languages?
Upvotes: 2
Views: 980
Reputation: 8810
Downcasts can be gradually eliminated by improving the power of the type system.
One proposed solution to the example you gave is to add the ability to declare the return type of a method as "the same as this". This allows a subclass to return a subclass without requiring a cast. Thus you get something like this:
public interface IEnumerable<T>
{
IEnumerator<T> GetEnumerator();
This<T> Filter( Func<T, bool> );
}
public class List<T> : IEnumerable<T>
{
// All of list's implementation here
}
Now the cast is unnecessary:
List<int> list = new List<int>( new int[]{1, 2, 3, 4, 5, 6} );
// Compiler "knows" that Filter returns the same type as its receiver
List<int> result = list.Filter( x => x % 2 != 0 );
Other cases of downcasting also have proposed solutions by improving the type system, but these improvements have not yet been made to C#, Java, or C++.
Upvotes: 2
Reputation: 189377
There are many languages with duck typing and/or implicit type conversion. Perl certainly comes to mind; the intricacies of how subtypes of the scalar type are converted internally are a frequent source of criticism, but also receive praise because when they do work like you expect, they contribute to the DWIM feel of the language.
Traditional Lisp is another good example - all you have is atoms and lists, and nil
which is both at the same time. Otherwise, the twain never meet ...
(You seem to come from a universe where programming languages are necessarily object-oriented, strongly typed, and compiled, though.)
Upvotes: 0
Reputation: 23332
Well, it's certainly possible to have programming languages that don't have subtyping at all, and then naturally there's no need for downcasts there. Most non-OO language fall into that class.
Even in a class-based OO language like Java, most downcasts could formally be replaced simply by letting the base class have a method
Foo meAsFoo() {
return null;
}
which the subclass would then override to return itself. However, that would still just be another way to express a run-time test, with the added downside of being more complicated to use. And it would be hard to forbid the pattern without losing all other advantages of inheritance-based subtyping.
Of course, this is only possible if you're able to modify the parent class. I suspect you might consider that a plus, but given how often one can modify the parent class and so use the workaround, I'm not sure how much that would be worth in terms of encouraging "good" design (for some more or less arbitrary value of "good").
A case could be made that it would encourage safe programming more if the language offered a case-matching construct instead of a downcast expression:
Shape x = .... ;
switch( x ) {
case Rectangle r:
return 5*r.diagonal();
case Circle c:
return c.radius();
case Point:
return 0 ;
default:
throw new RuntimeException("This can't happen, and I, "+
"the programmer, take full responsibility");
}
However, it might then be a problem in practice that without a closed-world assumption (which modern programming languages seem to be reluctant to make) many of those switches would need default:
cases that the programmer knows can never happen, which might well desensitivize the programmer to the resultant throws.
Upvotes: 1