twinbasic / lang-design

Language Design for twinBASIC
MIT License
11 stars 1 forks source link

Inheritance? #10

Open mansellan opened 3 years ago

mansellan commented 3 years ago

Is your feature request related to a problem? Lack of inheritance is often cited as the reason that VB6 isn't a "proper" OOP language.

Describe the solution you'd like COM is interface-based. It has no native concept of inheritance. But I wonder if this could be simulated with compiler support? Ultimately, all inheritance is based on implementation of the class's base interface (with automatic forwarding).

Additional context Inheritance would be a game-changer. Is it possible?

retailcoder commented 3 years ago

Lots of implications here.

A new Inherits keyword could be used to denote class inheritance.

Inheriting members from a base class means the compiler must issue warnings for member shadowing, and then derived types need a way to Override inherited members - should the inherited members be Virtual or Sealed by default?

Overload resolution needs to account for overrides along the inheritance hierarchy, and constructors need special attention too; if applicable, compiler should issue warnings when a Virtual member is invoked from a constructor unless the class is Sealed. Object construction needs to drill through the inheritance chain too, running the deepest base constructor first... which would be easier to achieve with a constructor chaining mechanism in place.

Do we introduce Abstract classes and methods too?

retailcoder commented 3 years ago

That said I'd love this to happen, be it only for the ability to implement templated methods.

bclothier commented 3 years ago

Addressing the COM side of things....

FWIW, in WinRT, there is also IInspectable which provides IInspectable::GetIIDs which basically enumerates all implemented interfaces. I point this out only to consider whether we should avoid re-inventing wheels.

The problem, though, is that VB* consumers won't know about that. Those interfaces will have to be discovered at runtime. However, this is to an element already the case. For example, several ActiveX controls say they derive from IDispatch and perhaps provide a main interface but they can additionally implement IOle*** interfaces. The only way to find out is via IUnknown::QueryInterface. That means there is nothing in COM that prevents a coclass from implementing a number of interfaces as it pleases.

That is all separate from the inheritance as traditionally understood in other languages and that is just implementation details that COM leaves up to the authors of the coclasses. However, we do need to make sure that we respect the expectations of COM/OLE/ActiveX/Automation when we expose our twinBASIC classes, regardless of how we've polymorhized it.

For example, to be compatible for Automation, The class has to promise that it is dual, and that all the data types it exposes via its COM interfaces are themselves Automation compatible. No exposing funky stuff like C-style arrays or ulong or that sort of thing (even though those are supported by the VARIANT struct and other non-Automation COM libraries do use them). In .NET world, we had the ability to specify how to expose the .NET object to COM without explicitly inheriting from IUnknown or IDispatch.

I think we also need to answer those questions:

1) should twinBASIC constraint itself to only Automation level of compatibility or allow it to work with any COM interfaces even though it might make it incompatible for use by VB* consumers? My vote is for ability to specify level of compatibility among with compile-time inspections to verify that the conventions is respected.

2) should the twinBASIC handle the COM's interface inheritance separately from the internal class inheritance? Coming from .NET, I'm comfortable doing each as a separate setup but others may feel this is extra clutters and don't want to be bothered with the low level details.

Regardless of the answers to the questions, the class inheritance within twinBASIC should be a private implementation detail and should not otherwise prevent authors from effectively reusing code within their coclass(es).

Kr00l commented 3 years ago

IMO a class (blank new) shall have only IUnknown and IDispatch in it's vtable to maintain compatibility. That's a fact and should be respected. Everything on top must be decided by a setting. But that would mess up the "simplicity" of tB.

WaynePhillipsEA commented 3 years ago

This feature is absolutely achievable, and an important feature to have. It would probably take about two weeks to implement and sort out all the side-effects and edge-cases.

WaynePhillipsEA commented 3 years ago

@Kr00l don't worry, the main class vtable won't be affected by such a feature.

mansellan commented 3 years ago

Thinking of the syntax that would be needed, here's a synopsis of VB.Net's inheritance keywords, with C# equivalents in brackets:

Class-level: Inherits (:) NotInheritable (sealed) MustInherit (abstract)

Member-level: Overridable (virtual) Overrides (override) NotOverridable (sealed) MustOverride (abstract) MyBase (base) MyClass (this) Shadows (new)

MyBase and MyClass are yucky, suggest Base and This instead? The rest look kinda OK to me, but maybe better to go with Sealed, Abstract and Virtual, what do you think?

retailcoder commented 3 years ago

I would definitely prefer going with Sealed, Abstract, and Virtual.

However Overrides lines up with Implements and Handles, so I'd prefer it over an upside-down override syntax.

This and Base would probably need to become reserved identifiers (please don't use my "This pattern" as the reason to go with the annoyingly childish MyClass/MyBase!), although there's probably no reason to block either as a UDT member name (but enum member should probably be blocked).

I'm leaning towards the nicely expressive Shadows over yet another meaning for the New token though.

mansellan commented 3 years ago

I would definitely prefer NOT to have members be default-virtual; the compiler should be able to optimise better if it doesn't routinely have to worry about polymorphism.

bclothier commented 3 years ago

I also reject the abomination that is MyBase and MyClass. I hope that we can recognize the difference between the This the type vs. This the variable. The downside is that this is actually a major departure from what other language might expect the this to be which is mapped to VB's Me.

I also agree that Shadows is much more descriptive than new.

I'm ambivalent about the virtual vs. Overridable. I don't like the verbosity but at the same time, calling it virtual feels weird especially if it has implementation which makes it look like a lie. I think if we consider the fact that all COM interface members are virtual, we might be better off just not worrying about whether a method is virtual or not and assume they are virtual. We can explicitly disallow virtual method by using sealed on the member. I think that is a more natural extension.

bclothier commented 3 years ago

Quoting from the roadmap issue as to not distract the thrust and keep discussion focused:

Btw, golang, rust and zig do not support implementation inheritance and prefer composition so I'm a little baffled by the introduction of (overloaded) constructors in TB too when a simple fix like being able to compile this


Set o = (New Class).ctor(...)

But that means the caller has to know that they need to call the ctor first before using anything else. Mathieu talked about temporal coupling and how that can be a problem. Furthermore, the constructor feature has nothing to do with inheritance. This can be implemented even if no inheritance was provided (as is the case right now).

Composition should still be preferred. In C#, that is mainly done by using interfaces. In COM world, we already are interfaces and therefore are biased toward compositing over inheritance, which is great! However, inheritance as a internal implementation detail has value in cutting down the amount of boilerplate code that would be abundant in a language without inheritance. In VB*, we are forced to do something like:

Implements IFoo

Public Sub Foo()
End Sub

Private Sub IFoo_Foo()
  Foo
End Sub

Or:

Implements IFoo

Public Property Get Foo As IFoo
  Set Foo = Me
End Property Get

Private Sub IFoo_Foo()
End Sub

When we really only wanted:

Implements IFoo

Public Sub Foo() Implements IFoo.Foo
End Sub

In this example, tB's interface implementation helps the readability of the codebase immensely. Less code (especially the boilerplate) = more readable, generally. We stand to similarly benefit from being able to inherit the shared implementation detail without having to repeat ourselves. And in this instance, I consider calling a shared function explicitly a form of repeating.

This doesn't mean we should build deep inheritance hierarchy or other outlandish design with OOP gone wrong. In my case, inheriting from one common base class is good enough for 90% of the use cases, allowing the base class to handle the shared implementation and the child class to focus on their specialized task, keep the number of lines of code small as possible and the contents tightly focused on the problem they are made for.

tB's language should continue to promote composition over inheritance but not to the point of excluding one other. Accordingly, the design of inheritance should promote composition (e.g. allow multiple interface implementation but only one concrete inheritance).

mansellan commented 3 years ago

Good point about COM @bclothier, but not all of twinBASIC will be COM-visible as discussed elsewhere. Java went the route of assuming virtual unless told otherwise, and IMO it was a mistake.

mansellan commented 3 years ago

That said, if Virtual is sub-optimal, I'd be fine with Overridable too. It's descriptive and arguably more newcomer-friendly.

wqweto commented 3 years ago

However, inheritance as a internal implementation detail has value in cutting down the amount of boilerplate code that would be abundant in a language without inheritance.

This is an architecural decision that only the language creators can make. I don't feel anyone outside core development can make such important decisions on which the fate of the project depends (not only this but in general) so I'm inclined not to comment or pesuade anyone about it but still wondering why would brand new languages like go and rust not have implementation inheritance then? Why are they back to POCOs with structs and skip on class keyword altogether? Do they promote code bloat in some way or just nudge developers to think outside OOP paradigm and find more FP solutions?

bclothier commented 3 years ago

I can't speak to Go as I am not familiar with it. With Rust, I understood that they use mixins/traits instead and that works better with their stated goal of making system programming easier & safer. Since they necessarily must work at lower level, OOP does not help them because that requires too much assumptions that would put them farther away from the metal.

FP is sometimes the correct solution. OOP is sometimes the correct solution. There is no One True Paradigm™ however as much as some proponents would like it to be.

I think the question to ask is -- will tB be better off using mixins instead of inheritance? I unfortunately don't have enough experience with mixins to provide an intelligent answer. Some might say "why not both?" but that is where multi-paradigms languages end up --- bloated with far too much design choices that the programmer is effectively paralyzed.

mansellan commented 3 years ago

FWIW, C# now has a mix of FP and OOP, and whilst in many regards it's great, IMO it's definitely added bloat and raised the barrier of entry for newcomers to the language.

retailcoder commented 3 years ago

IMO Visual Basic has a lesser leap to fully support OOP than it does to fully support FP. Personally I think even bare-bones inheritance that simply lets a class inherit the members of a base class (and inherits all base interfaces), even without the ability to override implementations or any other features, would be a huge win.

Supporting class inheritance doesn't rule out any future FP-friendly features, but not supporting inheritance leaves the language at the same not-quite-OOP level as VB6 and indeed, promotes rather bloated code because inheritance is missing.

IMO implementing class inheritance would simply be picking up where VB6 left off.

bclothier commented 3 years ago

Just to consider the other side...

Would tB be better off providing traits & mixins over inheritance? COM is itself heavily biased toward composition over inheritance and traits/mixins would make it easier to share common functionalities without force a strict relationship. After all it is not uncommon that a COM coclass may implement several interfaces (and not all interfaces may be directly exposed, particularly the IOle*** interfaces) that may be technically unrelated but composes together to do something (e.g. an ActiveX control).

References: SO thread on mixin vs. inheritance An article discussing advantages of mixins

wqweto commented 3 years ago

Speaking about COM and inheritance VB6 does not support aggregation which is a COM native feature regarding composition.

mansellan commented 3 years ago

Hmm... Is that the mechanism Sheet1 uses to acquire the functionality of a Worksheet?

bclothier commented 3 years ago

FWIW for contrast to the earlier question re: mixin, here is a counterargument against mixin.

mansellan commented 3 years ago

Maybe a design goal for twinBASIC could be to fill in the gaps in producing COM libraries - it is the underlying tech after all. Make it a first-class citizen, such that there's (little || nothing) that one has to resort to C++ for when targeting advanced COM facets?

So if aggregation is a blind spot for VB6, what others exist? I know very little about COM internals - @WaynePhillipsEA @wqweto @bclothier any thoughts? Also, if this is indeed a goal, perhaps it needs its own issue?

bclothier commented 3 years ago

Well, in the world of COM, everything's interfaces, basically. It's the CoClass that defines all interfaces (assuming that the CoClass deigns to tell others that it has those interface implemented). Therefore, this becomes an implementation detail.

The aggregation is basically just delegating an reference to another object. In .NET, we could do something like:

public class MySuperFoo : IFoo
  private readonly IFoo _theOriginalFoo;

  public MySuperFoo(IFoo foo)
  {
      _theOriginalFoo = foo;
  }

   void IFoo.DoFoo() => _theOriginalFoo.DoFoo()
}

That is conceptually same what the aggregation does and this can be achieved somehow even using VB* syntax though the code will become much more verbose than the C# snippet I just showed.

One thing that is not easily done inside VB*, however, is the ability to override the behavior of IUnknown::AddRef, IUnknown::Release and IUnknown::QueryInterface. In .NET, you can override the IUnknown::QueryInterface by implementing ICustomQueryInterface interface. Similarly, you can make a .NET object extendable by implementing IReflect (which is compatible with IDispatch-bound calls). The benefit of using ICustomQueryInterface and IReflect is that you get the benefit of type safety that you otherwise wouldn't have if you were implementing the IUnknown directly.

But, if you've noticed, all above relates to the interfaces. How we choose to handle interfaces that will be exposed to COM consumers and how we will describe our coclasses is totally up to us. That's the only thing twinBASIC needs to respect. However, that says absolutely nothing about whether my Foo inherits from Bar. That's an implementation detail that's beyond the concern of COM consumers. I can provide a IBar GetBar() if I want to, and that will be a part of the COM contract but that's my decision to do or not do. Whether the GetBar will be the "base" of the class or a unrelated class just doesn't matter to those consumers.

So that's why I see inheritance as solving a problem that's separate from how we will describe the COM contract. Ideally, twinBASIC should make it easy to not mix up those 2 parallel concepts. Whether we should provide the equivalent of ICustomQueryInterface or IReflect or allow direct overriding of the IUnknown and IDispatch is entirely a different discussion from inheritance, IMO.

vbRichClient commented 3 years ago

I'd leave inheritance (in the "classical sense") out of twinBasic - and instead introduce a new Keyword: Compose

When the compiler encoutners something like: Compose cSomeBaseClass it should automatically produce (or update):

In addition, the composition-host-class should also provide the ability, to change the "internally composed instance" from the outside, e.g. via:

Using this exposed Property will be required, in case a "pure Interface" was "composed" this way. For "instantiable, Project-internal Classes" the Compiler should:

I like this, because in almost all cases where I needed "inheritance", what I really needed was full aggregation of a BaseClass into a HostClass, including its Events.

This compositing-approach should allow for very sparse code in a given Host-Class (all the autogenerated Methods and Event-Redelegations are sitting in a CodeBehind-module). It allows (due to the exposed Public Prop of the Composed ClassName) an easy Dependency-Injection to influence behaviour of the Host.

And it would of course allow composition for multipe "behaviour-Classes" (including their Events). In case somebody wants to "override" something, he should be free to do that in the "CodeBehind"-section (the IDE should only update the signatures, when a certain Method-Symbol already exists in the CodeBehind-section).

My $0.02 with regards to "inheritance".

Olaf

bclothier commented 3 years ago

Olaf, it sounds like you're describing a mixin but using new words to describe it. As you said somewhere else, we should follow the existing art rather than introduce new concepts.

In this case, mixins allows for multiple behaviors to be shared by single class and avoids the diamond inheritance problem. However, I already posted links that argues and against mixins, the latter about the composition being better than mixins.

Furthermore, in C#, this can be approximated with an abstract class, though it enforces an inheritance hierarchy. However, when composing in C#, it would be typically done by using interfaces & private backing field as I explained in my previous post. Using expression-body statement, it can minimize the codebase without making it implicit. I personally think that implicit behaviors are the source of bugs or surprises so having an explicit expression, even if terse is far more preferred than introducing some behavior that is implicit and requires the programmer to read more beyond the text the programmer sees on the screen.

vbRichClient commented 3 years ago

it sounds like you're describing a mixin but using new words to describe it.

I don't really care what it is called (all the terms "aggregation", "mixin", "composition" can be interpreted and understood in a very wide manner, depending on the language - or the background you come from).

For my proposal (which includes "the aggregation of COM-Events as well"), there is no "prior art"... Why don't we go about it more "practically" (regarding "ease of implementation" also from the compiler-side).

What I've described can be implemented by a kind of preprocessor-routine (autogenerating normal VB-code) - without real changes on the compiler itself (regarding Class-compilation and Class-implementation of IUnkonwn and IDispatch). Keeping-It-Simple (and very robust at the same time).

Also the way the Implements-Keyword is currently handled at compiler-level will not be affected at all - this existing Implements-support can be used "in parallel" to the new Compose Declaration.

Furthermore, you can achieve quite a bit of flexibility with that approach (regarding later enhancements) - and "without breaking existing things, related to Composing", as e.g.:

Introducing free naming (and visibility) of the Private Variable which holds the BaseClass-instance within the HostClass, and whether Events should be composed or not: Compose WithEvents mBaseClass As BaseClass
With the above, the preprocessor will compose all "normal methods" - but doesn't compose Events, instead you are responsible to implement (and re-delegate them if needed) yourself, from within the HostClass "in the usual way". If you leave out the WithEvents-Keyword, all BaseClass-Events are automatically wrapped by the composer and raised to the outside.

Introducing free choice, to expose Properties to change the composed "behaviour-instance" on the HostClass or not: Compose mBaseClass As BaseClass DenyInjection
The above will now ensure, that "on the outside" there is no way to change the autocreated internal BaseClass-instance via Properties (no injection of differently implemented instances of the BaseClass-Interface-Type will be possible).

Introducing method-excludes for the Compose statement: Compose mBaseClass As BaseClass ExcludeMethods(bar) With the above, the composer leaves out Method "bar" from the auto-wrapping. This way, you are free to implement your own "bar-method" directly within the Host, or not. (and if you do, you can use your own Private-Variables, declared in the Host, to "hold state").

Introducing the same exclude-principle also to the Events of a certain BaseClass: Compose WithEvents mBaseClass As BaseClass ExcludeEvents(MouseMove) ExcludeMethods(bar) DenyInjection
With the above definition (going "all out"), the composer will ensure the following:

And in case the Composer is implemented in "all out mode" as decribed above - the preprocessor-generated "CodeBehind-Module" would not even need to be made visible in the Editor anymore - instead it could now be generated (or updated) temporarily and hidden in the compiler-workflow (since we can now explicitely decide, which methods and/or events we want to "handle ourselves, directly in the Host-Class")

I think, with something like that in place... whenever somebody asks, whether VB-New supports "inheritance", we could answer with confidence: "No, we have something more powerful".

If you want a practical demonstration "in code", I could zip-up an appropriate Demo written in VB6.

Olaf

WaynePhillipsEA commented 3 years ago

So my initial proposal here would be to offer a simple 'Via' keyword on the Implements statement

Class MyClass
   Implements MyBaseClass Via myField   ' all methods will be forwarded by the compiler to myField
   Private myField As MyBaseClass = New MyBaseClass

   ' but you can still override implemented members directly...
   Private Sub DoSomething() Implements MyBaseClass.DoSomething
   End Sub
End Class

Granted, this won't expose MyBaseClass privates from MyClass, but the above could be implemented quite easily, and would be a useful step in the right direction I feel.

Thoughts?

wqweto commented 3 years ago

Would forwarding field allow Private WithEvents myField As MyBaseClass so that the base class can raise PropertyChanged to aggregating child?

WaynePhillipsEA commented 3 years ago

@wqweto Yes, that will work too. The only restriction I intend to impose is that Via will only work on a class field or property. How that field itself is implemented (i.e. with WithEvents or not) should not matter.

wqweto commented 3 years ago

Btw, if the compiler is going to generate forwarding stubs it might be a good idea to allow these to be with public/friend visibility and keep (inherit) the target method names too -- an option which will mimic implementation inheritance quite a bit (but might lead to collisions on the default public interface).

bclothier commented 3 years ago

@WaynePhillipsEA just to clarify this bit:

The only restriction I intend to impose is that Via will only work on a class field or property.

Given:

Public Class Foo
  Public Event DidFoo()
End Class

This class has no fields/properties just an event which is not either from a semantic point of view. Therefore, if I:

Public Class Bar 
Private WithEvents Foo As Foo
Implements Foo Via Foo
End Class

But I will be able to do this, correct?

Private WithEvents Bar As Bar

Private Sub Bar_DidFoo()
End Sub

Furthermore, what if the Bar implements the DidFoo event and does not want to forward that event? Or, what if it implements the DidFoo but needs to then RaiseEvent it into its consumers for further processing?

Aside: I personally do not like it very much how VB generate default public interfaces. It's clutter and arguably the leading cause in the confusion over how to properly use interfaces in VB. I would not want tB to keep generating default public interfaces implicitly (though that may be a problem for backward compatibility). I should be able to fully control what interfaces my coclass will expose.

WaynePhillipsEA commented 3 years ago

@bclothier No, I wasn't planning on forwarding events to the outer class with my 'Via' proposal. Since the Implements statement only deals with the underlying default interface and doesn't do anything with events, I was simply planning to have the compiler forward all of the default interface methods.

My 'Via' version would take approx 2 days to implement, vs the 2 weeks or so needed for full on inheritance. I feel there are arguably more important things that tB needs right now, like GUI elements, than to take up 2 weeks on this at this stage. Or perhaps we just put this on the back-burner for now and wait to offer full inheritance support later.

bclothier commented 3 years ago

Ah, I understand. Incrementalism is great, but there's the risk of introducing a syntax that we are now stuck with because we didn't forecast the other use cases.

The Via proposal does sound like it will reduce so much pain that is acutely present when using Implements statement and I like that very much. If it's going to be a standalone feature and won't compose a part of the full inheritance proposal, then there should be a small risk and lot of benefits.

However, if people start expecting more, we might be in the untenable position of having to extend Via syntax beyond what it was intended and lead ourselves into awkward situations.

Greedquest commented 3 years ago

@bclothier FWIW, actually I don't think we need to be at all hesitant about introducing a new syntax feature only to remove it with breaking changes, this is after all an alpha preview - I think the value of previewing tB now is in part to build up a bit of hype and engagement, but mostly to stress test features in earnest, and breaking the syntax should not be confounding expectations and creating awkward situations but exactly what current users expect. In a later release I think your concern would be valid, but I don't think any of the doors we are going through right now are one-way and the cost of exploration is relatively low.

WaynePhillipsEA commented 2 years ago

For the Custom Controls side of things it was necessary to have some sort of Inheritance support, so I've now implemented my Implements-Via proposal described above. Here it is in action:

ImplementsVia

This will be a part of the next update coming tomorrow, and will help simplify custom controls going forward.

bclothier commented 2 years ago

Lovin' it! That will help cut down on the boilerplate coding that VBx loves to do...

Just a thought, though. I think we can simplify:

Implements MyBaseClass Via MyBaseClassField
Private MyBaseClassField As MyBaseClass = New MyBaseClass

to

Implements MyBaseClass Via MyBaseClassField = New BaseClass

I'm unclear whether the Implements-Via can be mutated at runtime. If we don't want that, then that version will help encourage avoid the accidental mutation of the MyBaseClassField. Even if mutation isn't an issue, having it on one line makes it easier to coordinate the refactoring and understand the code, I think.

WaynePhillipsEA commented 2 years ago

@bclothier yes the field is an ordinary field, so it is mutable, but I'm not sure it's particularly useful being so. Although swapping out the underlying base class at runtime does sound like fun.

The main thing you lose with the single-line version is the ability to use the WithEvents modifier on the field. I guess we could either incorporate it into the single-line syntax, or support both syntax and have the single-line version automatically include WithEvents if the class exposes events.

WaynePhillipsEA commented 2 years ago

Actually, in the single-line syntax we might want to only apply the WithEvents modifier when there are any of its events bound in the class. That way if events are not being listened to, we can avoid the extra overhead that comes with WithEvents, without needing it to be explicitly declared as such.

So with that in mind, I agree your single-line syntax will work better.

mansellan commented 2 years ago

I'm starting to wonder if the Via syntax may be worth keeping even if we do C-style inheritance later as well...

Let me explain.

In C#, implementing the Decorator Pattern involves a lot of boilerplate. You provide the object to be decorated into the constructor, then forward each and every member through to it one by one.:

public class LoggingWriter : IWriter
{
  private readonly IWriter _decorated;

  public LoggingWriter(IWriter decorated) // constructor
  {
    _decorated = decorated;
  }

  public void SomeIWriterMethod(string text)
  {
    _decorated.SomeIWriterMethod(text);
  }

  public void SomeOtherIWriterMethod(string text)
  {
    _decorated.SomeOtherIWriterMethod(text);
  }

  // etc for every other IWriter member...
}

Having a Via avoids all this boilerplate in a single flourish, if it could have the ability to selectively NOT automatically forward members that it wants to intercept.

bclothier commented 2 years ago

Regarding the base class swapping my concern is that this can make for some hairy tangling:

Example:

Class raises an event Consumer receives the event, takes a reference to the class's object member which happens to be the base object) Class then swaps the base object

Question: What is the consumer referencing now? The old base class or the new base class?

You can argue that this is our responsibility for not designing classes like that. On the other hand, you also can argue that tB should try and avoid setting up petards for unwary programmers to hoist themselves up. We already have C for that. 😆

I think that if the Via is basically COM aggregation under the hood, this should be fine since the reference is to the class, not to the base class and it'll be the class's responsibility for tracking the reference rather the base.

I might not be envisioning all the scenarios but just wanted to point out potential issues with teardowns when multiple objects are involved.

WaynePhillipsEA commented 2 years ago

Apologies, the next update is delayed by a day due to a new code-reducing experimental feature under way...

bclothier commented 2 years ago

If you need a fall guy for this, look no further! :smile:

WaynePhillipsEA commented 2 years ago

🤣 no... your one is already done, this one is something new...

WaynePhillipsEA commented 2 years ago

Here's your clue:

tbGenericEvents
wqweto commented 2 years ago

Btw, I just found out (following a random post on vbforums) that on Implements MyBaseClass it appears VBx emits a hidden variable Private MyBaseClass As MyBaseClass within the host class which remains empty (Nothing) but can be assigned with Set MyBaseClass = Me (or similar) so later a MyBaseClass.DoSomething call actually compiles and executes fine at run-time.

Didn't know about this undocumented member variable, probably a left-over and/or an implementation detail for IDE support of the editor combobox to be filled with the methods of the implemented interface but it looks kind of similar to what Via keyword aims at.

WaynePhillipsEA commented 2 years ago

As of v0.12.1, Implements-Via is now supported, offering partial inheritance support. Sample 6 (CustomControls) makes heavy use of this feature. Documentation to follow.

Kr00l commented 2 years ago

I just tested this feature and wondered that a duplicate definition occured for the DoStuff method.

Class SubClass
    Implements SuperClass Via Super = New SuperClass

    Public Sub DoStuff()
        Super.DoStuff
        MsgBox "more stuff"
    End Sub

End Class

Class SuperClass

    Public Sub DoStuff()
        MsgBox "basic stuff"
    End Sub

End Class

image

IMO that should be allowed to have the feeling of true inheritance. So when the caller has a new SubClass then the intellisense will of course only list's 1 DoStuff method. This should be called on the nearest chain, which is in the SubClass itself and not to the derived SuperClass.

With New SubClass
.DoStuff
End With

Also, can the SubClass somehow access the potential variables of the SuperClass ?