"Publicly Final" classes

Monkey Forums/Monkey Programming/"Publicly Final" classes

Samah(Posted 2015) [#1]
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?


Nobuyuki(Posted 2015) [#2]
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


Samah(Posted 2015) [#3]
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).


ziggy(Posted 2015) [#4]
That would be better by having immutable data structures, wouldn't it?


Samah(Posted 2015) [#5]
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.


marksibly(Posted 2015) [#6]
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.


Samah(Posted 2015) [#7]
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... :(


marksibly(Posted 2015) [#8]
You could always implement the base class setters as 'null ops' that either do nothing or cause an error.


Samah(Posted 2015) [#9]
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.


Nobuyuki(Posted 2015) [#10]
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