MX2 Classes

Community Forums/Monkey2 Talk/MX2 Classes

Danilo(Posted 2016) [#1]
What is the difference between Abstract and Virtual methods?
What is the difference between ordinary and Static classes? ("Class XYZ Static")

Need it for Irrlicht Import Tests.


impixi(Posted 2016) [#2]
I'm guessing some of MX2's semantics isn't yet fully functional.

You can declare a class as abstract or static. Yet these seem to make no difference to class behaviors/inheritance?
You can declare a method as abstract but this will not generate MX2 errors, yet will generate c++ compilation errors.
You can implement a virtual method in the class you declare it. Not sure if this should be possible or not: in some languages only subclasses can implement a superclass method declared virtual.

Class Uvw Extends Xyz
	Method DoSomething:Void()  Override
		Print "uvw"
	End
End

Class Xyz Abstract
	Field val:Int
	Method DoSomething:Void() Virtual
		Print "xyz"
	End
End


EDIT: BTW, I like your irrlicht progress. Keep it up!


Danilo(Posted 2016) [#3]
Thanks impixi! I found something about Virtual / Override in the MX2 Blog: Monkey1 to Monkey2
Virtual and overriding methods must be explicitly marked

_Note: This section highly subject to change._

In monkey1, all non-final methods are automatically virtual and can (sometimes inadvertantly) dynamically override existing methods. In monkey2, virtual and overriding methods must be marked as such, eg:
Class Base
   Method Update() Virtual
   End
End
 
Class Derived Extends Base
   Method Update() Override
   End
End


I guess an Abstract Class is a class where every method is Virtual then, and a Static Class
is a class without virtual members, or only when explicit declared as Virtual?
And what's the difference between Abstract Methods and Virtual Methods?
When importing virtual C++ functions, I always marked them as Abstract -
is this replaced with 'Virtual' in the future?

Well, the first Beta is probably (and hopefully) not so far away anymore... ;)


Richard Betson(Posted 2016) [#4]
I'm guessing some of MX2's semantics isn't yet fully functional.

I think so. I tried porting part of my framework and it seemed not yet there. Close though. :)


Danilo(Posted 2016) [#5]
Mark wrote on February 10, 2016 in Minor update on new compiler:
I’m currently in the process of converting the mx2 compiler from monkey1 to monkey2.
It can’t *quite* compile itself yet, but I think it’s about 90% of the way there and
I’m hoping to have it all up and running sometime next week.

What we use for testing is the state from 2 month ago. :)


impixi(Posted 2016) [#6]
Keep an eye on Mark's tweets too:
https://twitter.com/blitzmunter

Feb 11:

mx2 in mx2 just successfully parsed/semanted/translated itself! Now to fix a ton of compile errors..



Feb 12:

mx2-in-mx2 now parsing/semanting/translating/compiling/linking itself! *nearly* running...



Rebulild all' time for mx2cc has gone from 39s with old mx2 to 11s with new mx2!



Feb 14:

Yes! mx2-in-mx2 can build itself!




Danilo(Posted 2016) [#7]
Sounds good and is motivating. :)


Richard Betson(Posted 2016) [#8]
Keep an eye on Mark's tweets too:

Following ;)


marksibly(Posted 2016) [#9]
'Static' is gone in latest version which should be out 'soon'.

Methods now default to 'Final' unless you mark a class 'Virtual' or 'Abstract', in which case methods default to 'Virtual'.

This will be controversial, but having used this system for a while I have decided I like it. I still occasionally forget to mark something as 'Override' which is a little annoying, but the benefit of being able to look at some code and know the 'big picture' effect that modifying/adding/removing a method will have is worth it IMO.

A class marked 'Abstract' cannot be 'newed' (and it's methods default to 'virtual' as above) but this is all it does. This is based on Java, but I'm not sure how useful it really is...


Nobuyuki(Posted 2016) [#10]
I'm glad that at least you can mark an entire class Virtual instead of requiring explicitly marketing methods on both the base and subclass end. Being that explicit doesn't seem like something there's a clear unanimous consensus to doing amongst existing languages.

Next question: Will we still need to mark subclass overriding methods with the keyword Override if the superclass is marked Virtual? If so, can it be used to add any versatility to get around the "Overriding method does not match any overridden method" error, or is it just there for syntactical clarity / some kind of principle of 'good OO design'? Please forgive me if this is a silly question, I've never actually been educated in the principles of good OO design :)

Edit: VB.Net has a modifier keyword called Shadows which kinda does the "opposite" of Override -- it tells the compiler that the definition used in a subclass was created without regards to Liskov substitution. This protects subclasses from changes to the superclass breaking the subclass, and (iirc) may also hide those members from extensions of the subclass. I forget if this is a default modifier.

It seems like perhaps it would feel "less rigid" if the default overriding behavior (whether a method needs to be explicitly marked as an override or not) depended on whether or not the superclass was marked Virtual, but maybe that's opening a can of worms I don't realize? This is why I bring up the Shadows keyword, because in an ideal situation, I could mark my base classes Virtual to get essentially the same Monkey1 behavior and syntax for subclasses, and only have to use a keyword like Shadows to get around the "Overriding method does not match any overridden method" error.


taumel(Posted 2016) [#11]
Sometimes multiple inheritance can be great.


marksibly(Posted 2016) [#12]
> Will we still need to mark subclass overriding methods with the keyword Override if the superclass is marked Virtual?

Currently, yes. Adding 'Virtual' to a class only causes its methods to be marked 'Virtual' by default.

It *could* do more, but then this is starting to sound a lot like strict vs non-strict (can't believe I'm the one who wants 'strict' this time around!). I'll give this some more thought.

Never heard of that Shadows thing, but first impressions are 'yikes'. It appears to make method calls dependant on the static type of an object, so I don't really see how it would help in this situation. Example I looked at is here:

https://msdn.microsoft.com/en-us/library/ms173153(v=vs.80).aspx


taumel(Posted 2016) [#13]
... and make things easy (which might be the wrong motto for monkey2).


marksibly(Posted 2016) [#14]
> ... and make things easy (which might be the wrong motto for monkey2).

You means 'Shadows'? Could you explain how it solves the above problem(s)? I have not used it before, so it just looks weird/dangerous to me. Even the sample code seems to produce a 'wrong' result.


taumel(Posted 2016) [#15]
Hmm, when i read the above posts i was thinking about how easy it was/can be in certain languages (with multiple inheritance) to split stuff into basic classes with axiom like structs/methods, then define classes on an equal as well as on different levels and then build more complex objects out of them in a more natural way.

It's not perfect for all situations but it can offer a great bang for the buck in a number of use cases (like building atoms out of electrons, neutrons and protons, which again rely on all the quarks, you know, it's sometimes just easier doing such thigns in certain ways). It might not be the best answer for everything but in practice it's pretty good to get the 90% you mostly worry about done.


Nobuyuki(Posted 2016) [#16]
@marksibly
>Never heard of that Shadows thing, but first impressions are 'yikes'. It appears to make method calls dependant on the static type of an object, so I don't really see how it would help in this situation.

Perhaps I was thinking of c#'s "sealed". Default access level seems to imply that a subclass method with an identical name/arguments, but without the 'override' keyword will hide the base class's method and throw up a compiler warning about the hiding unless you explicitly specify the 'new' keyword in the method. (I may have been confusing this 'hiding' with shadowing!) I'm assuming that currently, you can't do this kind of base class method "shadowing" either way, and the error from monkey1 remains. I haven't tested it myself.

I'm not going to recommend using New for this particular behavior, because I'm sure it would be confusing (either to implement or for mx2 newbies), but if -- and this is a big if -- you take into consideration having Virtual classes change the default access modifier to not explicitly require Override, an explicit keyword to specify that a method isn't overriding the base class would be helpful. Semantically, one of those keywords ("sealed" or Shadows) came to mind, although perhaps neither really described what I was thinking of exactly.

---
I'm just trying to understand the nature of the scenario which lead to monkey1 not allowing a subclass to implement an identically-named method with a different signature. Under the hood, everything was marked virtual, right? Which I guess meant that there needed a guarantee that the behavior for subclass methods was identical across targets, which I'm assuming were always marked 'override' in languages where this was applicable. So now currently in mx2, we can specify Virtual or Static (static being the default so it's now redundant), but must also specify Override on the subclass end, lest we get behavior similar to the kind in your link. At least, that's how it would work in c#, but 'override' is not mandatory in c++, and I'll admit that I have no idea what the behavior is supposed to be like in that language, seeing as how 'override' was only introduced in c++11. I'm presuming that the answer is "it depends", and probably makes for a lot of needless complexity that we don't need to bring into mx2. Is this playing a factor in adopting the more c#-like "matching virtual/override" class behavior we currently see?

My ideal situation would be to minimize the number of modifier keywords needed for the majority of the methods being written, which as we talked about in a previous thread, depends on the person's coding style. It would seem that the most likely situation where someone would want to mark an entire class as Virtual would be to relax their forward-thinking caps a bit when writing the base class, making it easier/quicker to write subclasses containing a lot of overrides without regards to issues from things like this. But it would seem that bringing back this behavior (even just for classes marked Virtual) would also bring back the problems monkey1 had with subclass methods using the same name as the superclass needing to have a matching method signature. The only thing I could think of to satisfy my ideal preference without compromising mx2's flexibility would be a keyword which in these situations would bring back your preferred behavior, which (I'm assuming) is c#-like.

My apologies for the wall of text. I tried to explain my thoughts as specifically as I could considering my limited understanding of how OOP works in different languages.

>It *could* do more, but then this is starting to sound a lot like strict vs non-strict (can't believe I'm the one who wants 'strict' this time around!). I'll give this some more thought.

I appreciate you putting more thought into it. Thank you for your time.


Danilo(Posted 2016) [#17]
Mark would like to prevent the error/mistake of overriding a method by accident.
Without the keyword 'Override' you will get an error, so the system catches such mistakes.
Of course it may be annoying to explicit write 'Override' for every such method - but it's safe.

Without the keyword 'Override' it could happen you override a method by accident.
Without the keyword 'Override', to know whether a method is overriding something or not,
you have to lookup all parent classes and check whether the method already exists somewhere.

The problem with different method signatures is another thing. You can't simply override
method xyz(x:int) with xyz(x:int, y:int, z:int), because that could lead to stack problems/corruption.
If you add a method with a new signature, it must be handled as a new, separate method:
Class Base
    Method xyz:Void(x:Int)
    End
End

Class Extended Extends Base
    Method xyz:Void(x:Int, y:Int, z:Int) ' this method is a new one,
    End                                  ' it can't override Base:xyz
End

Class SuperExtended Extends Extended
    Method xyz:Void(x:Int)               ' this overrides Base.xyz
    End
    Method xyz:Void(x:Int, y:Int, z:Int) ' this overrides Extended.xyz
    End
End


Function Main()

    Local obj1:Base = New Base
    obj1.xyz(1)                          ' calls Base.xyz

    Local obj2:Extended = New Extended
    obj2.xyz(2)                          ' calls Base.xyz
    obj2.xyz(3,4,5)                      ' calls Extended.xyz

    Local obj3:Base = New Extended
    obj3.xyz(6)                          ' calls Base.xyz
    obj3.xyz(7,8,9)                      ' Error: Base does not have
                                         ' a method with signature
                                         ' xyz(int,int,int)

    Local obj4:SuperExtended = New SuperExtended
    obj4.xyz(1)                          ' calls SuperExtended.xyz(int) -  (overriden method)
    obj4.xyz(1,2,3)                      ' calls SuperExtended.xyz(int,int,int) - (overriden method)

    Local obj5:Extended = New SuperExtended
    obj5.xyz(1)                          ' calls SuperExtended.xyz(int) - (overriden method)
    obj5.xyz(1,2,3)                      ' calls SuperExtended.xyz(int,int,int) - (overriden method)

    Local obj6:Base = New SuperExtended
    obj6.xyz(1)                          ' calls SuperExtended.xyz(int) (overriden method)
    obj6.xyz(1,2,3)                      ' Error: Base does not have
                                         ' a method with signature
                                         ' xyz(int,int,int)

End



dmaz(Posted 2016) [#18]
This is based on Java,
In general, I think getting closer to Java is the wrong direction.. especially since you see the industry as whole moving away from it.
My ideal situation would be to minimize the number of modifier keywords needed for the majority of the methods being written
This!
strict vs non-strict
I do like the mx2 way using modifier keywords though better than just all encompassing strict modifiers.... but yeah
can't believe I'm the one who wants 'strict' this time around!
I actually kind of like your old thinking better :)

I haven't written anything big in mx2 yet... but my 2 cents are don't get closer to Java, C# or even c++ for strictness. IMO, these languages have some nice, cool things but overall they are terribly inefficient for coding because they require so much over-code.

A good language should allow the programmer to quickly and easily express his thoughts through little code while a good compiler( or debugging environment ) help to keep him from making stupid mistakes.


marksibly(Posted 2016) [#19]
> I'm just trying to understand the nature of the scenario which lead to monkey1 not allowing a subclass to implement an identically-named method with a different signature.

This was largely due to the multi-target situation. Many of the 'interpreted' targets don't allow overloading at all, so monkey1 has to fake it. Unfortunately, I did it in kind of a stupid way (in a crazy attempt to produce 'readable' code!) which ended up even worse when it came to overloading in combination with overriding.

But I was also kind of used to it myself. C++ has a similar limitation so I have tended to avoid overloading overrides in my own code for a while now, and honestly, I don't think it's a particularly bad practice.

Still, it is one of the most common complaints about monkey1 so it has been 'fixed' in mx2. It should in fact be working in the xmas demo, give it a try!

> My ideal situation would be to minimize the number of modifier keywords needed for the majority of the methods being written

Partly...but I also (now) believe that virtual methods should be used as sparingly as possible. They can introduce a lot of complexity, and they're slow. Final methods however are safe and fast - why not 'default' to being safe/efficient?

I guess this is more in line with the c++ philosophy of 'you don't pay for what you don't use', but I think it's a valid POV anyway. The efficiency thing is of particular concern when it comes properties - 90% of properties could and should be final if you care about speed at all, yet who does that? And I suspect the percentage of methods that could be final isn't much lower either.

Still, I strongly suspect(!) forcing explicit virtual/override down everyone's throat would be a bit much, so I had a hack it improving 'virtual classes' today and came up with the following:

* If you declare a class 'virtual', it behaves like a java/monkey1 class, ie: all methods are virtual unless you declare them final.

* You can't declare methods inside a virtual class as being 'Virtual' or 'Override'. The compiler effectively does this for you.

* All subclasses of a virtual class are also virtual, so you only need to declare the 'top' class as virtual.

This seems to work OK and I think would be appreciated by people from a monkey1/java/objectiveC background who are used to 'everything's virtual'.

It *is* a bit of a 'non-strict' style hack but I think I can justify that by claiming 'it enables alternative styles of programming'!


ImmutableOctet(SKNG)(Posted 2016) [#20]
@marksibly: That sounds like the best answer. I'm the guy who thinks dynamic casts and virtual calls should be avoided, but I also see their benefit from a design standpoint.

You've got to realize that a virtual method doesn't have to be called using a table. For example, if you're referring to a derived class directly, or you are calling your super-class's implementation from within an override. In these situations, it's pretty unlikely a C++ compiler won't optimize this. Of course, this depends on the tool-chain in question, but it's a pretty safe assumption with the major vendors.

There's also the inlining options C++ compilers take advantage of. Say for example you had a 'DataStream', and you passed it to a function or statically callable method that takes a 'Stream'. Under ideal situations, this will act as nothing more than an interface, and the type could be deduced using the caller's extra information. Now, there's a lot of details with this, for example, how the compilers handle link-time optimization, initial code generation, and AST exposition.

What I'm getting at is that virtual calls are sometimes an expressive encapsulation tool, rather than a purely polymorphic one. The virtual call behavior in Java is known for being expressive, but also quite fast. This is because the JVM can perform optimizations on the fly, reducing overhead from virtual calls. This is why people can get away with interfaces; both because they have the hardware to do it, and because they get benefits from their runtime that offset the design costs. Now, the static methods I brought up are also potential points of optimization for Java, but the point I'm trying to make is that there are ways to reduce the overhead without losing versatility.

With all of this in mind, I love the idea of controlling virtual and static methods in Monkey, so I think having both options is the best choice. Part of it's just a compatibility thing, but there's plenty of people who want to build systems that are easily extended.


As a bit of a tangent on the whole method behavior thing:

From what I've seen of function objects in Monkey 2, you can add functions to one like a list, right? Doesn't this open up a lot in the way of framework extensions? It's definitely got me excited. I'd love to make a system where people can simply hook into behavior I define. It's like a callback, only no one has to put a bunch of work into storage semantics.

This basically gives a whole different perspective on the whole extension method thing, doesn't it. Now I'm curious how well a language feature for this would go. Maybe something like an 'Extendable' modifier that lets people add to a method's "call-chain". This would also work with the ideas I've had about "method inheritance", where class A's method gets called, then its derived class's method gets called and so on. Basically what constructors do in C++. Maybe this would be simpler with an in-line lambda assignment for a field?

Ideas aside, this sounds great, and I can't wait for the next release.


marksibly(Posted 2016) [#21]
Virtual calls can sometimes be optimized out, but not often.

In the case of Super.Blah(), definitely, but beyond that, unless the compiler can somehow 'see' the New statement that was used to create an object, it can't know the precise type of the object and must call virtual methods via the virtual function table. So function params, fields, globals, and (most) locals cannot have any of their virtual method calls optimized away (unless the compiler does some super complex 'whole program' analysis - but even then...). C++ does allow you to 'select' a specific method via obj->class::method() but there's no syntax in mx2 for this and I wont be adding it!

Virtual method calls aren't *that* slow - just an extra indirect 'goto' (plus a bit more in the case of interface methods) - but they can't be inlined (except in the above cases) because the compiler doesn't know *what* to inline, which is the real cost speed-wise.

> From what I've seen of function objects in Monkey 2, you can add functions to one like a list, right?

Yep. The idea is pinched from c#, and I used to think it was a bit cheesy but it turned out to be so easy to do I couldn't resist! It effectively gives you something similar to JS 'addListener'.

But it's also something I would like mx2 to be capable of doing 'programmatically' one day, ie: it should be possible to call each function in a stack with a 'packet' (tuple?) of parameters. The flexibility to do this would enable a ton of other cool stuff.

> This would also work with the ideas I've had about "method inheritance", where class A's method gets called, then its derived class's method gets called and so on.

I had an idea like this a while back...

Method Update() Inherited 'note: calls Super.Update first!
End

...or...

Method Update() Synthetic 'note: calls Super.Update last!
End

...but that's a bit naff. All in all, living with 'hoping' derived classes call Super.Blah() if/when necessary is probably as good as it gets.

The most interesting idea I've ever seen (not mine!) like this is to force base classes to call derived class methods, eg:

Method Update()
'do some stuff...
Derived.Update() 'complete opposite of Super.Update()!
'do some other stuff...
End

The gives the base class method complete control over when the derived class method is called - if at all!

This gets around the whole Update()/OnUpdate() pattern that many coders use to ensure that virtual methods have any appropriate setup done before they are called (which only works to one-degree of 'virtual-ness' anyway - you can end up with OnOnUpdate).

Definitely too out there for mx2, but it's an interesting idea...


Gerry Quinn(Posted 2016) [#22]
I'm generally a fan of strictness even at the cost of prolixity, but I like the idea of a choice between making class methods either virtual or static by default.


dmaz(Posted 2016) [#23]
> This would also work with the ideas I've had about "method inheritance", where class A's method gets called, then its derived class's method gets called and so on.

I had an idea like this a while back...

Method Update() Inherited 'note: calls Super.Update first!
End

...or...

Method Update() Synthetic 'note: calls Super.Update last!
End

...but that's a bit naff. All in all, living with 'hoping' derived classes call Super.Blah() if/when necessary is probably as good as it gets.

The most interesting idea I've ever seen (not mine!) like this is to force base classes to call derived class methods, eg:

Method Update()
'do some stuff...
Derived.Update() 'complete opposite of Super.Update()!
'do some other stuff...
End
I would argue against all 3 of these... "method inheritance" takes the control away from programmer. Inherted/Synthetic both replace one line in the body for another keyword. why? that one super call is clear and concise and allows me to call it at the start, end or somewhere in the middle(which comes in handy). Also, when looking at somebody else's code I don't have to stop, scroll back to the top of the class or look at the base classes to know how this method is implemented.

as for avoiding virtual calls and properties.... I'd call that premature optimization. mx2 has structs, so there you go!


so a virtual method is just an extra goto.... what is the performance implication for an extension method vs a final method? And, since extension methods are really just plain functions can we use them to extend structs?


Nobuyuki(Posted 2016) [#24]
Still, I strongly suspect(!) forcing explicit virtual/override down everyone's throat would be a bit much, so I had a hack it improving 'virtual classes' today and came up with the following:

* If you declare a class 'virtual', it behaves like a java/monkey1 class, ie: all methods are virtual unless you declare them final.

* You can't declare methods inside a virtual class as being 'Virtual' or 'Override'. The compiler effectively does this for you.

* All subclasses of a virtual class are also virtual, so you only need to declare the 'top' class as virtual.

This seems to work OK and I think would be appreciated by people from a monkey1/java/objectiveC background who are used to 'everything's virtual'.

It *is* a bit of a 'non-strict' style hack but I think I can justify that by claiming 'it enables alternative styles of programming'!


Hooray! Sounds keen, can't wait to try the next demo.


DruggedBunny(Posted 2016) [#25]
Short related article found via Hacker News, "Swift final methods under the hood" -- comes to a similar conclusion:

https://medium.com/@MarcioK/swift-final-functions-under-the-hood-2deccd0b9437

(Er, copy and paste the URL!)


dmaz(Posted 2016) [#26]
I don't at all agree with that final conclusion
Next time that you create a method and you are not expecting to be overridden remember to mark as final.
"not expecting"... if you really want to close your source like that (see what I did there:) you should have specific reasons that have nothing to do with the minor performance gain.

This is still a huge fight but the pendulum that has been swung to very strict is starting to relax. Many (myself included) will argue that in some environments strictness like that has not brought the efficiency promised but actually has brought the opposite.


marksibly(Posted 2016) [#27]
...bit of a rant, but I find this interesting...!

> "not expecting"... if you really want to close your source like that (see what I did there:) you should have specific reasons that have nothing to do with the minor performance gain.

The most obvious benefit of 'locking down' methods with final is that it ensures a method always does what you think it will do everytime it's called, and that it hasn't been hijacked by an overriding method that may or may not cooperate in vague, unspecified ways (eg: remember to call Super.Blah/NOT call delicate methods X,Y,Z during OnRender etc etc). There is a huge benefit to methods that 'do this - and ONLY this!', for both class authors and users. Such methods are also much easier to change, as you don't have to worry about upsetting the expectations of overriding methods.

> if you really want to close your source like that (see what I did there:) you should have specific reasons that have nothing to do with the minor performance gain.

I think pretty much the opposite (and it's nothing to do with performance). A virtual method breaks encapsulation, so shouldn't be done lightly and without planning. Making all methods virtual is IMO similar to making all fields public. It's a helluva convenient, but you can no longer guarantee that someone's not messing things up behind your back. The whole idea of a 'class invariant' gets seriously weakened. It also makes it harder for people extending classes to know what is 'safe' to override and how/when. Do they need to call 'Super'? After or before they do their own thing? Are there any methods unsafe to call during overriding? etc etc. By carefully planning what can be overridden and what can't, you can avoid a whole bunch of FUD.

And once a virtual method is out there, you're stuck with it. You've got make sure it has the side effects and works pretty much the same way forever (sort of similar to maintaining a 'public field'), as overriding methods will depend on it doing so. And if it's called by another method, that method also needs to call it forever too etc. This is especially true if you write something for an 'external' audience - once a method is virtual and someone has overridden it in a project, there's no going back. Final methods on the other hand can be trivially made virtual without any chance of affecting existing code, so final is IMO clearly the 'safe' option if you plan to mix virtual/final methods.

Using Qt has affected me here somewhat here I think. It has some quite complex classes, but few virtual methods. This actually makes it very easy to extend Qt classes, because there are only a few things you *can* override, unlike, say, Cocoa where you just blindly try overriding methods 'until it works'. Perhaps this makes Qt less flexible than Cocoa? I haven't found this to be the case though, as Qt tends to provide explicit ways to do stuff Cocoa depends on 'stunt overriding' to achieve. I prefer this as a user as it's much clearer how to achieve XYZ without having to get into the 'guts' of superclasses etc. And it no doubt makes life much easier for the Qt coders.

But I have really been sold on the point of 'default final' and explicit virtual/override by using it in the new (unfinished - but it wont change much!) 'View' class and the new mx2 compiler. For starters, I find it incredibly useful to be able to tell at a glance whether a method is virtual, override or neither. Whenever I see 'virtual', I know a 'new method' has been added to the class hierarchy at this point, and that modifying the method will potentially affect subclasses. Ditto with 'override', I know that this method is implementing a 'service' provided by a superclass. And best of all, default final methods I know I'm pretty much free to mess with without having to think about super or sub classes at all. It allows me to think much more locally about what I'm doing than if I just see 'a method' and have to stop and think about where it fits into the hierarchy and the potential side effects of modifying it. Yes, it does require a bit of extra self-discipline and a bit more verbosity, but for me anyway, the payoff is worth it.

Of course, not everyone's writing compilers and guis, and Cocoa clearly works and people do cool stuff with it - and the convenience factor of having everything virtual is not to be sniffed at by any means! - so I don't really consider myself 'right' here. It's just based on my own experiences and successes/failures, and no doubt people with different experiences will have reached different conclusions.

This whole topic is really part of an apparently long running debate between 'designed inheritance' and 'open inheritance' proponents, and we appear to be on opposite sides here! There's quite a good description of the issues here (although I don't agree with all his points):

http://martinfowler.com/bliki/DesignedInheritance.html

This guy prefers open inheritance too, but that's cool - if there is an active debate over this I feel more comfortable providing the 'virtual classes' feature.

> Many (myself included) will argue that in some environments strictness like that has not brought the efficiency promised but actually has brought the opposite.

Examples?

IMO, the #1 reason c++ is still king of game languages is it's efficiency, which is probably mostly due to it's ability to inline stuff effectively, which in turn depends on a certain degree of strictness to achieve, eg: static typing, non-virtual methods etc.

Unity is possibly a curious exception though. I've played several Unity games on ps4 which have performed, erm, less than optimally, which kind of surprises me as the engine is pretty kick ass and c# *should* produce efficient code as it is quite like c++ in terms of 'helping the compiler out' via static typing etc. Still, having never written anything for the ps4 myself I am not exactly qualified to comment!


taumel(Posted 2016) [#28]
C++ is here because it's almost everywhere, too big to go away (soon), lots of libs/existing code/... and what you said but for something like monkey2 i would gave up on 10% efficiency if this could save me 90% of time (but don't get me wrong efficiency is cool). I'm in for whatever makes sense, things easier, fun, beautiful and lets me get things done.

Unity is great in terms of its physical based shading, enlighten, its fmod integration, ... When it comes to aspects like the game engine, mono, not so much anymore (at least you can replace MonoDev with Sublime). If you can handle it, Unreal provides a more solid experience.


Gerry Quinn(Posted 2016) [#29]
C++ efficiency also comes from compatibility with C. I don't care what people say about the wonders of modern compilers, or modern processors being too complicated for human optimisation - the right coder hitting the metal can still get the best results. C++ combines this C compatibility with enormous expressiveness. It's like it can hit the metal in terms of abstraction too!

Of course something has to give, and it is simplicity and ease of use. But it won't go away until either Moore's Law kicks in again (not gonna happen) or the AIs get smart enough to program themselves.


dmaz(Posted 2016) [#30]
Making all methods virtual is IMO similar to making all fields public.
you mean as default... well that comparison does remove performance as a consideration so yeah, it comes down to style or as your link names it, "Software Development Attitude". I totally know where you are coming from but since I have an Enabling Attitude, I almost completely disagree :) The one point I'll concede up front is that once a method is virtual clearly it is more difficult make it final then the reverse.

Final methods are fine and useful but I feel that suggesting they should be the default without any thought, design or reason is basically saying:
"I don't want to think about how you may want to extend my class so... you can't!"
If you don't want to think about it, then let me do what I need to do without resorting to extension via copy paste class duplication.

You might get less support headaches from a bunch of final classes or you might just get a bunch of emails asking for more features... or since the downstream programmer can't express his creativity or enhance his productivity, you might get no emails since he found a different solution. One of the things you've mentioned a few times is it's more difficult for you to make changes once people start overriding methods in unforeseen ways. I don't really accept this... how is it any different than the ways users get upset. Users really have 2 solutions, they update their extensions or they don't upgrade to your new classes.

I haven't worked with or even looked at Qt but my guess is the library is probably went through very extensive design and planing. They probably built the classes specifically with the idea that they would be extended so they designed them in a way to facilitate that. I have no problem with this philosophy, but this can actually be a lot or work sometimes.

Now I actually like override... though not how you've implemented it.(?) The keyword is clearly good information to have in the extended class and should be required no matter the default of it's super.... correct me if I'm wrong but override will not be required and actually can't be used if the super is declared virtual, right? I don't like that becuase now I can't tell by looking at the extended class alone whether a method is an override or addition.

IMO, the #1 reason c++ is still king of game languages is it's efficiency, which is probably mostly due to it's ability to inline stuff effectively, which in turn depends on a certain degree of strictness to achieve, eg: static typing, non-virtual methods etc.
actually the efficiency I was referring to was specifically that of the programmer... not compiler or executable. I know a static, strongly typed compiler will produce more efficient exes. But this is at the expense of too much code... though on the other hand, too dynamic or weakly type can result in long bug hunts. I posted an interesting link to a paper a while back that talked about good middle grounds. I'll see if I can find it again.

to sum up since we are talking about the compiler here too and not just libraries... I'm a firm believer that the language shouldn't get in the way, It should facilitate creativity not inhibit it. So if the library/class writer decides to close a class, fine but I do think the compiler should default to more open / less strict

here's a good discussion on final... the comments, not the blog post itself :)
https://ocramius.github.io/blog/when-to-declare-classes-final/
here's one comment that sums up some of my experience pretty well



Richard Betson(Posted 2016) [#31]
I have been following along and for my two cents methods should always be 'Final' by default. Adding a descriptor to the method would be my preferred choice. ;)


Michael Flad(Posted 2016) [#32]
In my opinion code should be easy to read, understand and reason about which means, as explicit as possible and so I'm on the final by default camp too.

If you had to dive into a bigger project as a new coder and there's layers and layers of abstractions and probably everything is virtual you have a really hard time really knowing (instead just guessing and hoping) what's going on in various situations.


dmaz(Posted 2016) [#33]
In my opinion code should be easy to read, understand and reason about
that's my opinion as well... but for me, I get that from the code being short and concise (without tricks). lots of layers of abstraction or inheritance can certainly be have code smell and I'm no fan of that. But trying to fix all other issues by short circuiting the programmer I would label extreme.


Shinkiro1(Posted 2016) [#34]
I completely agree with Mark here. Also, that you gain performance for the 'final by default' is imo just a nice side effect.
The important thing though, is that an API gets more predictable, you have to think less about all the depencies and it's more often what you expect.

Compare the 2:
- Final by default
Calling foo.bar() will call the base class bar() method.

- Virtual by default
Calling foo.bar() will call the base class bar() method, or ANY method which extends the base class which has overridden it.

I prefer case 1, where you explicitly state Virtual as: I expect this method to be overridden and changed, the class will still function no matter what is done inside the method.


Nobuyuki(Posted 2016) [#35]
dmaz: A lot of thought's been put into the whole Final/Virtual thing, this isn't the first thread it's been talked about for sure. This is why I'm happy that Mark's implemented the functionality to replicate monkey1's behavior with a strategically-placed keyword. The arguments for making Final default seem "sound enough" to me, although it isn't my style of coding. Similar behavior exists in other languages, notably C#.

Monkey2 is a new language, and users should have to upgrade their code to suit that, particularly libraries and modules. A haphazard port would allow for many unintentional side effects. There's no huge base of public code which people depend upon preventing changes to the syntax to "fix" perceived problems in the last iteration of the language, like there was with Python or Perl.

>I don't like that becuase now I can't tell by looking at the extended class alone whether a method is an override or addition.

Perhaps "Override" could be an optional method keyword if the base class is marked Virtual. Optional verbosity in that case I think wouldn't hurt anyone, it's similar to people (like myself) who really didn't like code blocks all ending with simply "End".


ImmutableOctet(SKNG)(Posted 2016) [#36]
I'm just going to say this now: A difference in philosophy and design practice do not equate to "strict vs. non-strict" linguistic rules. One is a long-running debate about design choices, and one is a conformity toggle. This analogy needs to die here. They aren't the same thing, and the topic's been around for many years now. There's two sides to this argument, and unless Mark changes his mind, I'm pretty sure we're getting something akin to 'Virtual' classes.

I've already written not too long ago that from a software design and extendibility perspective, Monkey's existing system works great. It's not so much "virtual-by-default" to me, it's "extensible-by-default". This is coming from me, the guy who is happy about more control over virtual calls, types, and the stack. The same guy who has a history of agreeing with the philosophies of languages like C++. I'm someone who's been asking for lower-level features in Monkey for years. Despite my thoughts and background, I find "virtual-by-default" (Or whatever you want to call it) a useful tool. More specifically, it's a tool that's easy to implement and lets existing code be ported a lot easier.

I've already written and worked with modules that would benefit from both design practices. In general, I've found that Monkey's current extensibility has worked very well for these modules. Does that mean we shouldn't get the option of full control? No, I think that's a great idea, but I also don't think the API developer always knows how their code will be used. It's always a good idea to plan ahead and remain consistent with an API, but that doesn't mean you should stunt extension.

There's several reasons why Monkey 1's default library 'monkey' hasn't been as capable as other languages. The first problems being the large oversights and lack of a 'Protected' keyword early on. There's also problems like the lack of proper interfaces or "views", making container abstraction an even bigger challenge. Most importantly, we have little to no way of extending these classes, because their methods and/or data (Fields) are completely locked down. I think this already shows that you have options to restrict users when using "virtual classes", even when unintended. In this case, it would have been justified with proper interfaces, but you get the idea. Sometimes, inheritance isn't the best answer.

You've got to understand that not everything's black and white. You can build an API that's as restrictive or as extensible as you want, using either method behavior. The real issues come from what people prefer from a design standpoint. There is the efficiency level, but it's the compiler (In the case of "virtual-by-default") and library developers' calls whether something should be done one way or another. You can change the default behavior in this case, but the simple fact is that it's already a design choice, and some people prefer an abstraction above virtual calls.

Monkey already gives you this choice right now, and it's called 'Final'. I don't see why switching the defaults would restrict your level of control. The only thing it does is change what default practices are allowed, and because this is a serious topic on both sides, I honestly think a separate keyword is perfectly fine. We're using this language for abstraction where it counts, it's the same reason to use C or C++, so would giving people more abstraction options be so bad?

I'm not arguing for either at this point, but if Mark is willing to add the current behavior as an option, I don't see a problem with it.


dmaz(Posted 2016) [#37]
My issue with final as the default, is that most classes will end up being, final by default and not by design.... if you are going to lock me out, fine, but design the class and give me the tools to make that work. I wager that's not what will happen... most classes will just go with the default until the class creator finds a use that requires a method to be virtual. This will hamper usability... Here's the thing... final by default is the only option that takes options away. And if marking a class virtual excludes the use of override, I see even more use of final.

do we need a default?... here's an area where I would welcome being more explicit! :) It wouldn't bother me for methods to require virtual/final/override