Add class support
This is not going to be trivial, partially since our current `TypeReference` implementation presumes that variables and functions always return a valid CLR type. Well, a _class_ being defined in the global (or local) scope, isn't a CLR type (at least not until we make the move to make all Perlang classes exist as valid CLR types as well - this move will come at _some_ point to make Perlang code interop with existing .NET code properly, being able to implement .NET interfaces by Perlang classes etc), so that's at least one of the challenges. There will probably be numerous others as well. Here's the Crafting Interpreters chapter about the `jlox` implementation of classes: https://craftinginterpreters.com/classes.html. It doesn't help us _that_ much since the static typing support we added in !54 has made things much more complex. We are already quite different from `jlox` and there is unfortunately no-one apart from ourselves that we can ask for help about how things work in our code base... ## Initial features (supported in 0.1.0) - [x] Calling `static` C# methods to support things like `Base64.encode` and `Base64.decode` (initially exposed as `base64_encode` and `base64_decode` but moved to class-based static methods in !79) ## Features likely to get implemented, but not as of the initial effort (0.2.0 or "Later") - [x] User-defined classes: define classes in Perlang code and be able to instantiate them. - This was started in the 0.1.0 milestone, but the code that existed was removed in !183. Adding it here for reference. We should put it back, and make it work equally well as in Lox. - This is now being re-added in !573. - [ ] C#-defined classes: be able to instantiate classes defined in C#. This would be very useful and help us to add nice things like an `IntList` (mutable `List<int>`, for lack of generics) - We'll need a `new` keyword for this or similar, unless we go the Ruby approach and just tack `.new()` _after_ the class name. Making constructors be nothing else than plain static factory methods does have a few advantages, but it makes things like `final` fields (which must be initialized statically or in the constructor) way harder to achieve. - `new` keyword implemented in !573, but supporting classes defined in C# is not supported. - [ ] Inheritance: while this is nice and a fundamental in OOP, this is a "more advanced" feature and might get implemented a bit later. We are not hostile to OOP in any way, so I am not opposing it, just deferring it slightly. - Not yet supported - [ ] Visibility: `public`, `private`, `protected`, `internal`, `protected internal` (similar to package-private in Java, which unfortunately can only be specified by _not_ explicitly giving a visibility. This is one of the design deficiencies of the Java language from my perspective) - Not yet supported; at the moment all classes and methods must be explicitly `public`. All fields must be explicitly `private`. By enforcing the visibility specifier, we are forward-compatible with potentially supporting other visibilities going forward (and explicit is better than implicit. :slight_smile:) ## Features that are likely to not ever get implemented (i.e. "non-goals") - Multiple inheritance. While useful sometimes, it can cause the [diamond of death](https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem) problem and in general, increases complexity of a computer system without adding enough value to warrant the cost. ## Implementation details ### Implemented parts (included in 0.1.0) - [x] ~Be able to define classes (!73).~ Disabled in !183. - [x] Detect multiple classes with the same name, throw an exception if this happens (!73) - [x] Support for calling static methods (only on native .NET classes at the moment) (!73) - [x] Support for calling static methods with parameters. (!79) - [x] Support for defining classes in "native" .NET code, and static methods on these. (!79) ### Implemented in 0.7.0 - [x] Support for instantiating classes: !573 - [x] Support for calling methods on instances: !573 - [x] Support for mutable instance fields: !582 - [x] Support for immutable instance fields: !583 - [x] Validating that all defined fields are initialized either via initializer or constructor: !583 ### Implemented in 0.8.0 - [x] Support for defining static methods: !641 ### Remaining parts - [ ] Support for public fields - (I'm hesitant as to whether to implement this or not, since it breaks the basic idea of encapsulation. We could go with "all fields must be private in classes" and "all fields must be public in structs", to keep some form of separation between those, but will it make sense? Particularly if we start considering `struct` to be a value type, making that aspect also control whether fields should be public or not might feel very messed up and non-ergonomic.) - [ ] Support for instance properties - [ ] Support for static fields and/or properties - [ ] Support for `abstract` classes - classes with one or more `abstract`, unimplemented methods (or inheriting from an interface but not implementing one or more methods) - [ ] ...probably a bunch of other things as well
issue