Page 1 of 2
A language without downcasting
Posted: Thu Feb 12, 2015 3:59 pm
by AndrewAPrice
Can you think of an example where an object oriented language without downcasting would be a sever hinderance?
Cat implements Animal, and you can cast Cat to Animal, but you can't cast Animal back to Cat because you can't statically verify at compile time that Animal will be a Cat.
The only example I can think of it is if a container that allows elements of any type, even then I'm sure you could get around it. For example (in psuedocode):
Code: Select all
interface CatOrDog {
void IsCatOrDog(void (*isCat)(Cat *cat), void (*isDog)(Dog *dog));
}
class Cat : CatOrDog {
void IsCatOrDog(void (*isCat)(Cat *cat), void (*isDog)(Dog *dog)) {
isCat(this);
}
}
class Dog : IsCatOrDog {
void DoSomething(void (*isCat)(Cat *cat), void (*isDog)(Dog *dog)) {
isDog(this);
}
}
Container <CatOrDog> containerOfCatsAndDogs;
CatOrDog animal = containerOfCatsAndDogs[0];
animal.IsCatOrDog(void (Cat *cat) {
print("this is a cat");
},
void (Dog *dog) {
print("this is a dog");
});
But, this required modifying Cat and Dog to implement CatOrDog and add a method IsCatOrDog, which if you had a lot of collections of different combinations, you might end up with some really messy code (and in a formal software development environment, your team may not appreciate you adding new methods to other classes). Can you think of another way around this without downcasting?
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:05 pm
by kzinti
Python doesn't have any down casting (or any casting for that matter) and it's not a problem. Neither does Javascript. Nor does a whole bunch of other languages...
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:14 pm
by AndrewAPrice
A downcast could be done through a 'type switch' which casts the variable down to the subtype within the case block, although this doesn't actually eliminate downcasting, it just wraps it:
Code: Select all
interface Animal { }
class Cat : Animal { }
class Dog : Animal {}
Container <Animal> containerOfCatsAndDogs;
Animal animal = containerOfCatsAndDogs[0];
type (animal) {
Dog {
// 'animal' is of type 'Dog' inside of this scope
}
Cat {
// 'animal' is of type 'Cat' inside of this scope
}
default: {
print("unknown animal");
}
}
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:15 pm
by HoTT
Python doesn't have any down casting (or any casting for that matter) and it's not a problem. Neither does Javascript. Nor does a whole bunch of other languages...
Because Python and Javascript have no static type system.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:33 pm
by SoulofDeity
This is one reason I personally dislike encapsulation. When you use encapsulation, the code effectively becomes owned by the data and all who inherit the interface of the data. Some languages support traits or multiple inheritance to work around this, but that just sidesteps the problem.
My belief is that the cleanest and best solution is just to accept the existence of a semantic gap. Disconnect yourself altogether from the notion that there is a structure; everything is an object; no if ands or buts. This includes methods. Once you think this way, inheritance is not a problem. Types are dynamic, and as long as the 2 objects have the same fields there is nothing stopping you from using the same method on objects that aren't related in any way.
Summed up, polymorphism trumps inheritance.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:49 pm
by AndrewAPrice
SoulofDeity wrote:2 objects have the same fields there is nothing stopping you from using the same method on objects that aren't related in any way.
I'm actually going with duck typing. If an object implements an interface, you can cast it to that interface:
Code: Select all
interface Animal {
void Talk();
}
class Dog {
void Talk() { print("Woof"); }
}
class Car {
void Drive() { print("Vroom"); }
}
Dog dog;
Car car;
Animal animal1 = dog; // works!
Animal animal2 = cat; // error - Car doesn't implement Talk();
It actually makes the design of my language and standard library easier. There is an interface "Number" which defines the basic math operators + - / * < > <= >= so you can make generic template math functions.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 4:56 pm
by SoulofDeity
Out of curiousity, does your implementation also cover cases where 2 or more interfaces have overlapping fields? This is a case I can only see as being solvable through use of reflection.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 5:05 pm
by HoTT
It actually makes the design of my language and standard library easier. There is an interface "Number" which defines the basic math operators + - / * < > <= >= so you can make generic template math functions.
Only reference types or how do you handle assignment of types that are actually of different sizes?
Re: A language without downcasting
Posted: Thu Feb 12, 2015 8:02 pm
by Rusky
If a design needs downcasting it's (according to the priests of OOP) a bad design anyway. It tends to be a workaround most of the time- a better solution is usually to find a way to statically specify what a piece of code needs, usually by using a more specific interface.
But that sort of talk can't get very far with typical "Animal, Cat, Dog" examples of OOP. What exactly is the language going to be used for? What sort of paradigms do you want it to enable? For example, languages like OCaml, which don't have downcasting, do have pattern matching on algebraic data types.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 8:15 pm
by AndrewAPrice
SoulofDeity wrote:Out of curiousity, does your implementation also cover cases where 2 or more interfaces have overlapping fields? This is a case I can only see as being solvable through use of reflection.
I was actually going to copy how Go implemented interfaces, which is to store the interface pointer as two values - a pointer to the actual object, and a pointer to a lookup table that is unique for that combination of class and interface. The interface member is an offset into the lookup table, which is an offset to where that field is in the object or where the method is located. The compiler needs to track everytime a class is casted to an interface so it can be sure the lookup table exists.
In a language with static typing, this can be done by the compiler with no runtime support or reflection.
The only time you'd need to track the type at runtime is to downcast (which you could probably store in the lookup table), that's why I was asking if it would even be necessary to support this,
Re: A language without downcasting
Posted: Thu Feb 12, 2015 8:32 pm
by AndrewAPrice
HoTT wrote:It actually makes the design of my language and standard library easier. There is an interface "Number" which defines the basic math operators + - / * < > <= >= so you can make generic template math functions.
Only reference types or how do you handle assignment of types that are actually of different sizes?
Interface variables - as references internally. I think it would complicate things to store dynamic sized objects on the stack, however I will use escape analysis to see if the underlying object escapes the scope of the function to know if I can point to it on the stack or if I have to allocate it on the heap.
For templates classes and functions - it doesn't matter, because you'd implement sqrt<Number> using + - / * etc, then if you call sqrt<float>, sqrt<double>, sqrt<int>, or your own complex number class it'll compile that class or function with that.
Re: A language without downcasting
Posted: Thu Feb 12, 2015 9:28 pm
by SoulofDeity
MessiahAndrw wrote:The only time you'd need to track the type at runtime is to downcast (which you could probably store in the lookup table), that's why I was asking if it would even be necessary to support this,
This in particular is what I was thinking about. Supposing you have a method that operates on interface A that has field X. This code is compiled and distributed as a closed source library.
Someone else creates a new interface B that also has field X (where we're assuming here that X is still the same type). If they want to use this library to create a new object that implements both interface A and B, how does the method in question know how to downcast the object to manipulate X? This is a case where I think X would need to be resolved at run time through reflection.
Re: A language without downcasting
Posted: Fri Feb 13, 2015 12:13 am
by AndrewAPrice
SoulofDeity wrote:MessiahAndrw wrote:The only time you'd need to track the type at runtime is to downcast (which you could probably store in the lookup table), that's why I was asking if it would even be necessary to support this,
This in particular is what I was thinking about. Supposing you have a method that operates on interface A that has field X. This code is compiled and distributed as a closed source library.
Someone else creates a new interface B that also has field X (where we're assuming here that X is still the same type). If they want to use this library to create a new object that implements both interface A and B, how does the method in question know how to downcast the object to manipulate X? This is a case where I think X would need to be resolved at run time through reflection.
Regarding closed source libraries:
In languages like C and C++ that use header files, the headers that come with the library describe the objects inside. More advanced languages that don't use headers tend to embed metadata into their module/library files, so you know what data structures are inside.
For your specific example:
1) The code inside of the library would pick the best overloaded method to use at the time the library was compiled, because the application includes the library, not the other way around.
2) Do you mean if Cat implements Animal and Pet, and we had two possible overloaded functions DoSomething(Animal) and DoSomething(Pet)? The compiler could say it doesn't know which one to pick and ask the programmer to cast it to the one they want first.
Re: A language without downcasting
Posted: Fri Feb 13, 2015 12:29 am
by SoulofDeity
MessiahAndrw wrote:Regarding closed source libraries:
In languages like C and C++ that use header files, the headers that come with the library describe the objects inside. More advanced languages that don't use headers tend to embed metadata into their module/library files, so you know what data structures are inside.
The problem is with code generation. There is ambiguity. In order to manipulate the object, it must be passed by reference to the method. This means that the method itself needs to downcast the object. (The alternative is to duplicate the object into a separate structure, which is stupidly slow and resource-heavy). When defining the structure of an object, implementing interface A, B produces different code than implementing B, A. The method may try to access X of A and end up actually getting Y of B because both interfaces have X and it expects implementation A, B but is given B, A.
Re: A language without downcasting
Posted: Fri Feb 13, 2015 12:57 am
by Combuster
Rusky wrote:If a design needs downcasting it's (according to the priests of OOP) a bad design anyway.
Priests are never good at betasciences:
Code: Select all
class Node implements TreeObject
{
K key;
TreeObject left;
TreeObject right;
}
class Leaf implements TreeObject
{
K key;
V value;
}
Have fun dealing with that datastructure without violating at least one Holy Commandment of Pure OOP.