"Publicly Final" classes
Monkey Forums/Monkey Programming/"Publicly Final" classes
| ||
Mark, I have a use case where it would be useful to make a class non-final within its module, but final when imported from another module. Example: color.monkey: Public Class Color ' Publicly Final, Privately non-Final? Public Global RED:Color = New ImmutableColor(255, 0, 0) Global BLACK:Color = New ImmutableColor(0, 0, 0) Private Field r%, g%, b% Public Method Red:Int() Property; Return r; End Method Red:Void(r:Int) Property; Self.r = r; End Method Green:Int() Property; Return g; End Method Green:Void(g:Int) Property; Self.g = g; End Method Blue:Int() Property; Return b; End Method Blue:Void(b:Int) Property; Self.b = b; End Method New(r:Int, g:Int, b:Int) Self.r = r; Self.g = g; Self.b = b End Method Mutable:Color() Return Self End End Private ' this should be fine, because Color should appear non-final within color.monkey Class ImmutableColor Extends Color Final ' Always Final Public Method Red:Void(r:Int) Property; Error("Color is immutable"); End Method Green:Void(g:Int) Property; Error("Color is immutable"); End Method Blue:Void(b:Int) Property; Error("Color is immutable"); End Method Mutable:Color() Return New Color(Self.r, Self.g, Self.b) End End main.monkey: Import color Function Main() Local color1:Color = New Color(0, 255, 0) Local color2:Color = New MyColor(0, 255, 0) Local color3:Color = Global.RED End ' this should fail compilation, because Color should appear to be final outside color.monkey Class MyColor Extends Color Method New(r:Int, g:Int, b:Int) Super.New(r, g, b) End End I can't see this as being particularly hard to implement in trans, if a keyword were picked. Thoughts? |
| ||
Wondering if there is a reason this isn't implemented in other languages, it seems limited in usage scope? Perhaps make your color class private for within the module, and only expose a final public extended class..? Something like ColorBase or Color_ for the internal one |
| ||
The problem with that is that you'd have to declare any variables as ColorBase to make them compatible with both subclasses. I want them to be interchangeable without having a secret base class. An interface would work, but it seems ugly for developers to define a variable with an interface if they'll only ever create the one kind of concrete class (the immutable one is internal). |
| ||
That would be better by having immutable data structures, wouldn't it? |
| ||
That would be better by having immutable data structures, wouldn't it? True, but I want them to be mutable. Having to create a new Color instance (or get one from the pool) every time you want a new colour is painful. The ImmutableColor class is specifically for system colours that the developer shouldn't be able to change. I know it's not the ideal design, but it was more of a thought experiment as to if it could be done. |
| ||
Interesting idea, but probably a little harder to implement than it appears. One the main reasons I added final was so that the c++/c# translators could use non-virtual methods if possible, allowing for more inlining etc. Allowing the above would make this a bit tricky. Another option in this case would be to swap the mutable/immutable hierarchy: Class Color 'const, ie: immutable Public Global Red:=New Color( 1,0,0 ) Method New( r:Float,g:Float,b:Float ) Private Field r:Float,g:Float,b:Float End Class MutableColor Extends Color 'mutable Public Method Cycle:Void( time:Float ) Method SetRed:void( r:Float ) 'etc End So a plain Color is immutable, which is probably fine 90% of the time. Most methods that use a color wont be modifying it, just using it, and those that do modify a color can take a MutableColor, which clues you in that the color will be changed by the method. This is what Cocoa does with it's standard lib container objects, and I've always liked it, eg: Class String Class MutableString Extends String Class Array Class MutableArray Extends Array Class Dictionary Class MutableDictionary Extends DIctionary It's pretty much the nearest you can get to using immutable objects everywhere without having to create lots of temps. I guess if you wanted to get really specific, you could do something like: Class Color Abstract 'ctors and getters only... End Class ConstColor Extends Color Final 'just seals the color class End Class MutableColor Extends Color 'adds setters End This is similar to Nobuyuki's common base class idea, but I don't see any reason to make anything private. Then, methods could dictate what sort of color they wanted: Method SetColor:Void( color:Color ) 'don't care if color is mutable or immutable Method SetColor:void( color:ConstColor ) 'gimme a const color Method SetColor:Void( color:MutableColor ) 'gimme a mutable color ...but I think this is overkill, Color/MutableColor are probably good enough. |
| ||
Another option in this case would be to swap the mutable/immutable hierarchy: This is exactly what I had before, but I wanted to use properties. I had getter properties for the base class, and setter properties in the mutable class. However, this would not compile because in Monkey you can't overload an inherited method. I wish you would fix this... :( |
| ||
You could always implement the base class setters as 'null ops' that either do nothing or cause an error. |
| ||
You could always implement the base class setters as 'null ops' that either do nothing or cause an error. That's what I did, but I was hoping for a compile error. |
| ||
That's what I did, but I was hoping for a compile error. That actually sounds like something more interesting. In C++ you can use a StaticAssert (or static_assert, depending on the lib) to enforce that kind of behavior, and it appears that C# can handle this sorta thing with code contracts: http://blogs.msdn.com/b/bclteam/archive/2008/11/11/introduction-to-code-contracts-melitta-andersen.aspx https://msdn.microsoft.com/en-us/library/dd264808%28v=vs.110%29.aspx |