There are a few things to consider when designing a class system: Inheritance, access modifiers, scopes, nested classes, virtual members, etc.
Inheritance can be fairly straightforward, at least in a statically typed programming language. In fact, inheritance can be easy to implement even in dynamically typed languages. Multiple inheritance can be harder, but it's still not that hard. Provided that you can figure out at compile time what the base class(es) is/are, things are simple enough.
Now, let's add access modifiers and scopes to the mix.
Initially, let's consider only two access modifiers - public and private.
Public is very easy to implement. Private can be more complex. First of all, we need to define what private means. One way to define it is a member that can only be accessed from a scope that's descendant of the class scope it is declared in.
In statically typed languages, this is easy enough.
However, consider this hypothetical situation in pseudo-code:
class Foo {
private var x;
public function bar(othervar) {
this.x = othervar.x;
}
}
Can we do this?Let's say this language is statically typed and othervar is of type Foo. Then sure we can. The scope of bar is inside the scope of Foo, which is the type of othervar.
Now, let's say this is a dynamically typed language. We can not, at compile time, know if othervar is Foo or not. In addition, we don't know if othervar.x is private or public.
This means that, at runtime, we have to find out if othervar.x is private, and if it, we must find out if Foo has permissions to access it private othervar members. This may not be trivial.
But it gets worse.
Let's add protected to the mix. Protected members also allow access from derived classes, so it gets harder to check.
Let's say we define that protected members of a class can also be accessed from nested classes of derived classes. This makes sense, but only makes things even harder.
Finally, virtual members. Class Derived extends Base. Base defines a virtual member virtualfoo and Derived overrides it.
Let's consider the following code in a hypothetical statically typed language:
Base bar = new Derived();In the above case, because virtualfoo is virtual, Derived.virtualfoo is called.
bar.virtualfoo();
In a dynamically typed language, the exact same thing happens, without any problem nor ambiguity.
Also, even in this case, the Base.virtualfoo must still exist somewhere in case Derived does something like "base.virtualfoo()"(super.virtualfoo in Java).
Now, what about members which aren't virtual?
Base bar = new Derived();Some languages may not allow the above to begin with. C# allows it, but includes a keyword "new" to ensure it is intentional.
bar.nonvirtualfoo(); //Where nonvirtualfoo is defined in both Base and Derived.
In a dynamically typed language, this can be implemented, but even if it works, it wouldn't be intuitive.
Now, if both Base and Derived include a nonvirtualfoo() member, but Base's is private and the one in Derived is public, then it would make more sense, but still be problematic.
Here's what I thought so far:
1. Classes are important to have and so is inheritance
2. The public access modifier and virtual members are in.
3. The private access modifier and non-virtual members are at risk.
4. The protected access modifier is out.
One thing I thought, inspired by some JavaScript programming, would be to have - instead of private - something even more private. Some sort of instance-private, that could only be accessed with the "this" keyword. And perhaps some sort of inherited-private, that could be called also with the "super"/"base" keyword.
I haven't decided this yet, but it's clear that a conventional class/access modifier would not be intuitive nor efficient in PineDL. These are just some ideas on how to solve the problem.