As rule of thumb I try to keep all my classes as const classes. For services it means they can be reused between threads / web requests and for entities / DTOs it means they can be passed through Actors and stored in a cache. (It can also reduce complexity knowing an object is never in a dirty state.) Code is simple and it gives me a warm fuzzy feeling.
Then there are mixins; interfaces with optional method implementations. Mixins are great for abstracting out behaviour. They can do most things except be instantiated or hold state. As a contrived example, my entities will often implement a Displayable interface:
Now what strikes me as odd with the mixin above is the word const. For a construct that doesn't care about state, because it can't hold any, why should it need, or care about, being const or not?
This was further highlighted when I converted one of my entities to be normal, or non-const. This entity couldn't use Displayable anymore, because it was no longer const, and Displayable, well, is!
Well, not really, because I still can't cast to a common parent. It's frustrating to think that a such simple interface forces all my entities to be const, or non-const; which is quite a fundamental design choice.
Why?
I understand why the const key word is needed; for static typing and defining const classes.
const class Example {
const Displayable displayable
}
The Example class can only be const if Displayable (be it a mixin or class) is defined as const. So for a class to be const it needs to know at compile time that all its fields are also const classes.
As an interesting aside, if a mixin is never used as a field, but only passed around in method signatures:
Void display(Displayable displayable) { ... }
Then nobody really ever cares if Displayable is const or not.
Maps and Lists
The glaring exceptions to all this are Maps and Lists, which bring in the concept of immutability. Maps and Lists are not const, but can be immutable. This is somewhat an abstract concept because currently, user defined classes can not be immutable.
Resolution
I'm just thinking out loud here... (and fishing for an answer to my directionless diatribe!)
It'd be nice if the const mixin paradox could be resolved, and preferably without changing Fantom too much.
To that end, I have 2 ideas:
Runtime Const Field Checking
If in the case of a const class definition, when a field declaration is found to be a mixin, defer the const check until runtime.
My initial reaction to this was, "Woah, that'll bring in heaps of bugs!" But then, after scrutinising my code, I found very few instances where I declare fields with a mixin type.
User Defined Immutable Classes
Maybe by whatever means (a keyword, a class level facet or a system mixin) we could declare a class or mixin as immutable, that is, they can become immutable by implementing toImmutable().
Then we wouldn't have const mixins anymore, but immutable mixins. Still a bit strange but being immutable is much less of a constraint than being const.
Those are my thoughts, I'd be interested to hear other people's take on it.
brianTue 15 Apr 2014
If in the case of a const class definition, when a field declaration is found to be a mixin, defer the const check until runtime.
I've been back on forth for this. But I always come back around to that the immutability of a type really is one of the most important aspects of its signature. I've found having const mixins all typed checked at compile is actually what I want virtually all the time.
BTW there is a simple (if bit ugly work around too):
mixin Foo {} // non-const mixin
const class Bar
{
new make(Foo f) { fooRef = f }
Foo foo() { fooRef }
private const Obj fooRef
}
that is, they can become immutable by implementing toImmutable().
I definitely have wanted this too, and was tempted to make it virtual earlier this year. But the problem is how to you actually make it work with any sort of sane compile time and/or runtime checking? I'd love to hear some design proposals
SlimerDudeTue 15 Apr 2014
BTW there is a simple (if bit ugly work around too)
Cheers Brian, I didn't think of hiding a non-const declaration in an immutable Obj.
It still doesn't let me const class Bar : Foo { ... } though... : (
toImmutable() ... I'd love to hear some design proposals
I need to go wake up my other brain cell for this... it may take some time!
SlimerDudeFri 13 Mar 2015
toImmutable() ... I'd love to hear some design proposals
Some belated thoughts...
Run Time
Run Time behaviour is easy - just assume that all objects could become immutable. The standard toImmutable() behaviour would then also:
attempt to perform a deep clone by calling toImmutable() on all storage fields
Any class that can not be immutable (usually classes with native storage, like Buf) would override toImmutable() to throw an Err.
Compile Time
But as mentioned, it would be nice to know at compile time if a class is immutable or not. (This is the difficult part!)
A problem with mixins is that they're unaware of their implementation. Given that most Fantom classes just contain Lists, Maps, Strs and Ints and other immutable types, how about assuming all types could be immutable until proved otherwise.
Then we could have some sort of Mutable keyword / interface / facet that marks a Type as mutable. Similar to how const works, if a class contains a field that is a mutable type, then it too must be marked as mutable.
The toImmutable() behaviour would then also contain:
if any field is a mutable type, throw NotImmutableErr.
Unless explicitly marked mutable, mixins and super classes are assumed to be toImmutable()'able. Implementations and subclasses could have mutable fields, but that doesn't affect the parent types. (*)
Rationale
Fantom's unique compile time Type inference puts faith in the developer and says if a Type could be coerced then it can. This catches 99.9% of errors at compile time and leaves the other 0.1% to be caught at run time.
I see the above (*) idea being similar. For compile time checks, we assume the developer is right, and if a class could be immutable we allow it. All other checks are then deferred to run time.
Addendum
Maybe mutable could be held (mainly) as an internal flag - to prevent the any proliferation of mutable keywords?
SlimerDude Tue 15 Apr 2014
Some thoughts on
const mixins
...As rule of thumb I try to keep all my classes as
const
classes. For services it means they can be reused between threads / web requests and for entities / DTOs it means they can be passed through Actors and stored in a cache. (It can also reduce complexity knowing an object is never in a dirty state.) Code is simple and it gives me a warm fuzzy feeling.Then there are mixins; interfaces with optional method implementations. Mixins are great for abstracting out behaviour. They can do most things except be instantiated or hold state. As a contrived example, my entities will often implement a
Displayable
interface:The Paradox
Now what strikes me as odd with the
mixin
above is the wordconst
. For a construct that doesn't care about state, because it can't hold any, why should it need, or care about, beingconst
or not?This was further highlighted when I converted one of my entities to be normal, or non-const. This entity couldn't use
Displayable
anymore, because it was no longerconst
, andDisplayable
, well, is!Could I use 2 mixins?
Well, not really, because I still can't cast to a common parent. It's frustrating to think that a such simple interface forces all my entities to be
const
, ornon-const
; which is quite a fundamental design choice.Why?
I understand why the
const
key word is needed; for static typing and definingconst
classes.The
Example
class can only beconst
ifDisplayable
(be it a mixin or class) is defined asconst
. So for a class to beconst
it needs to know at compile time that all its fields are alsoconst
classes.As an interesting aside, if a
mixin
is never used as a field, but only passed around in method signatures:Then nobody really ever cares if
Displayable
isconst
or not.Maps and Lists
The glaring exceptions to all this are Maps and Lists, which bring in the concept of immutability. Maps and Lists are not
const
, but can be immutable. This is somewhat an abstract concept because currently, user defined classes can not be immutable.Resolution
I'm just thinking out loud here... (and fishing for an answer to my directionless diatribe!)
It'd be nice if the
const mixin
paradox could be resolved, and preferably without changing Fantom too much.To that end, I have 2 ideas:
Runtime Const Field Checking
If in the case of a
const
class definition, when a field declaration is found to be a mixin, defer theconst
check until runtime.My initial reaction to this was, "Woah, that'll bring in heaps of bugs!" But then, after scrutinising my code, I found very few instances where I declare fields with a
mixin
type.User Defined Immutable Classes
Maybe by whatever means (a keyword, a class level facet or a system mixin) we could declare a class or mixin as immutable, that is, they can become immutable by implementing
toImmutable()
.Then we wouldn't have
const
mixins anymore, butimmutable
mixins. Still a bit strange but being immutable is much less of a constraint than beingconst
.Those are my thoughts, I'd be interested to hear other people's take on it.
brian Tue 15 Apr 2014
I've been back on forth for this. But I always come back around to that the immutability of a type really is one of the most important aspects of its signature. I've found having const mixins all typed checked at compile is actually what I want virtually all the time.
BTW there is a simple (if bit ugly work around too):
I definitely have wanted this too, and was tempted to make it virtual earlier this year. But the problem is how to you actually make it work with any sort of sane compile time and/or runtime checking? I'd love to hear some design proposals
SlimerDude Tue 15 Apr 2014
Cheers Brian, I didn't think of hiding a non-const declaration in an immutable
Obj
.It still doesn't let me
const class Bar : Foo { ... }
though... : (I need to go wake up my other brain cell for this... it may take some time!
SlimerDude Fri 13 Mar 2015
Some belated thoughts...
Run Time
Run Time behaviour is easy - just assume that all objects could become immutable. The standard
toImmutable()
behaviour would then also:toImmutable()
on all storage fieldsAny class that can not be immutable (usually classes with native storage, like
Buf
) would overridetoImmutable()
to throw an Err.Compile Time
But as mentioned, it would be nice to know at compile time if a class is immutable or not. (This is the difficult part!)
A problem with
mixins
is that they're unaware of their implementation. Given that most Fantom classes just contain Lists, Maps, Strs and Ints and other immutable types, how about assuming all types could be immutable until proved otherwise.Then we could have some sort of
Mutable
keyword / interface / facet that marks aType
as mutable. Similar to howconst
works, if a class contains a field that is a mutable type, then it too must be marked asmutable
.The
toImmutable()
behaviour would then also contain:mutable
type, throwNotImmutableErr
.Unless explicitly marked
mutable
,mixins
and super classes are assumed to be toImmutable()'able. Implementations and subclasses could havemutable
fields, but that doesn't affect the parent types. (*)Rationale
Fantom's unique compile time Type inference puts faith in the developer and says if a Type could be coerced then it can. This catches 99.9% of errors at compile time and leaves the other 0.1% to be caught at run time.
I see the above (*) idea being similar. For compile time checks, we assume the developer is right, and if a class could be immutable we allow it. All other checks are then deferred to run time.
Addendum
Maybe
mutable
could be held (mainly) as an internal flag - to prevent the any proliferation ofmutable
keywords?