A language without downcasting

Programming, for all ages and all languages.
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

A language without downcasting

Post 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?
My OS is Perception.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: A language without downcasting

Post 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...
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: A language without downcasting

Post 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");
  }
}
My OS is Perception.
HoTT
Member
Member
Posts: 56
Joined: Tue Jan 21, 2014 10:16 am

Re: A language without downcasting

Post 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.
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: A language without downcasting

Post 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.
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: A language without downcasting

Post 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.
My OS is Perception.
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: A language without downcasting

Post 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.
HoTT
Member
Member
Posts: 56
Joined: Tue Jan 21, 2014 10:16 am

Re: A language without downcasting

Post 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?
User avatar
Rusky
Member
Member
Posts: 792
Joined: Wed Jan 06, 2010 7:07 pm

Re: A language without downcasting

Post 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.
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: A language without downcasting

Post 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,
My OS is Perception.
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: A language without downcasting

Post 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.
My OS is Perception.
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: A language without downcasting

Post 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.
User avatar
AndrewAPrice
Member
Member
Posts: 2299
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Re: A language without downcasting

Post 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.
My OS is Perception.
SoulofDeity
Member
Member
Posts: 193
Joined: Wed Jan 11, 2012 6:10 pm

Re: A language without downcasting

Post 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.
User avatar
Combuster
Member
Member
Posts: 9301
Joined: Wed Oct 18, 2006 3:45 am
Libera.chat IRC: [com]buster
Location: On the balcony, where I can actually keep 1½m distance
Contact:

Re: A language without downcasting

Post 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. :wink:
"Certainly avoid yourself. He is a newbie and might not realize it. You'll hate his code deeply a few years down the road." - Sortie
[ My OS ] [ VDisk/SFS ]
Post Reply