Ok, so I've been digging through Scala's traits and some interesting reading material and have this to report. First off Scala's traits aren't really traits. For trait to be a trait in accordance too Traits composable units of behavior[pdf] it has to:
Provides a set of required and offered methods;
Can't specify state variables, nor use the state variable directly;
Classes and traits can be made from other traits but the order of their usage doesn't affect them;
Conflicting methods must be explicitly resolved;
When you add a trait to a class your adding an additional layer of methods over the super class. Anything you override in your class takes precedent over traits
Example:
class A { override Str toStr() { return "A"}
trait TA { override Str toStr() {return "TA"}
class B1 : A {}
class B2 : A,TA {}
class B3 : A,TA { override Str toStr {return "B3"} }
echo(B1) // A
echo(B2) // TA
echo(B3) // B3
Composing a trait from other traits also doesn't affect its semantic.
When looking at Scala's trait the biggest beef I have with it is linearisation and state variables in traits. This means that class behavior depends on order in which they are called so class A: Student, Worker is not the same as class B: Worker, Student. Even somewhat bizarrely it is the rightmost trait that is most important.
Analogous I propose we make mixins a bit more flexible and possible add some kind of keyword for their runtime combining.
Proposed syntax:
I propose we use & sign for adding dynamic. Why I chose the symbol? Brevity. I can be replaced with : or with or something else.
mixin Foo
{
Void greet() { echo("I'm Foo!";}
}
mixin Bar : Foo
{
override Void greet() {return "I'm Bar!";}
}
class A
{
foo := Obj & Foo
foo.greet // I'm Foo!
bar := Obj & Bar
bar.greet // I'm Bar!
}
This compiles into Java as
interface Foo { void greet();}
interface Bar extends Foo{ void greet();}
abstract class FooClass implements Foo
{ void greet() {System.out.println("I'm Foo!");}}
abstract class BarClass implements Bar
{ void greet() {System.out.println("I'm Bar!");}}
class A
{
private class ObjFoo extends FooClass{}
private class ObjBar extends BarClass{}
ObjFoo foo = new ObjFoo();
foo.greet();
ObjBar bar = new ObjBar();s
bar.greet();
}
Difference between today's mixin and the mixin I propose is somewhat superficial. "Dynamic" mixin would automatically recognize abstract field and override them while creating a default constructor if possible:
mixin VolumeControl
{
abstract Int volume
Void incrVolume { return ++volume;}
Void decrVolume { return --volume;}
}
class Radio
{
Int volume
}
radioWithVolume := Radio & VolumeControl
Possible issues: This might be dog slow. What I noticed Scala often does in times like this is make VolumeControl : Radio in order to staticaly get its field. It might be worth to do a couple of experiments to see what approach is best from a performance standpoint.
Conflict resolution
Ok but how do we sort conflicts that arise once we have a collision? Let's go back to previous example and say we want to make:
foobar := Obj & Foo & Bar //ConflictingSlotErr must resolve Foo.greet and Bar.greet conflict
foobar2 := Obj & Foo & Bar
{
override Void greet() { echo ("I'm FooBar!");}
}
Alternative conflict resolutions:
But previous example leaves a lot to be desired for. First it's a lot of boiler plate code, almost as bad as writing whole class. Let's say we don't want to print Im a Foo!' for FooBar.
foobarBar := Obj
& Foo -greet //just remove this slot from inheritance
& Bar
foobarBar.greet() //I'm Bar!
Alternatively we want it to print both "I'm Foo!\n I'm Bar!". In that case
gives small easily composable units of behavior (for most part) unrelated to class hierarchy.
Cons:
their dynamic behavior may cause slower execution
more complex than current mixins
I've concluded the dynamic mixin proposal. Here is my take on how we can use it.
Further thoughts
This is independent of the existing proposal. We might want to take a page from Scala's book and implement one rule that isn't specified in the above pdf. What I mean is that we allow for a trait to "extend" a class. What this means is simple:
Such trait gains all of extended class' slots.
Such trait can only be applied to extended class or its children.
Any mixin that tries to call class' slots will call this instead.
Example:
class List
{
void add
void remove
V first
V last
}
trait Stack : List
{
V pop
void push
V peek
}
stack := Int[,] & Stack
However this has the problem of introducing of having to deal with one more level of slot collision. The problem can be solved in two ways:
Allow only one trait that extends a class in (dynamic) mixin definition (e.g. if Sortable : List and Immutable : List then defining SortableImmutable : Sort, Immutable would cause a compiler error). Less complex and more constrained. Probably a better initial design.
Allow for another level of mixin collision. In this more elaborate case here is what happens:
So in addition to standard rules of mixin composition shown earlier (e.g. sort := Int[,] & Sortable & Immutable -add) here are some ideas I had in mind
sortImmute1 := Int[,] & Sortable & Immutable & Stack
{
add(V e)
{
retVal := this.dup
//Now there are several solutions to this...
**#1 just regular mixin language
i := Sortable.super.place(e)
retVal.addAt(i,e)
**#2 if extension methods are added then treat them as extension methods
return Sortable.super.add(retVal, e)
**#3 some custom syntax
return retVal.Sortable.add(e)
return retVal.add[Sortable](e)
}
}
This proposal for extending classes would save us a lot boilerplate code but it is rather complex. It's an advanced feature I might elaborate upon later.
brianWed 7 Sep 2011
Thanks for taking the time to write up such a complete proposal!
I think the fundamental idea here is that mixins as designed in Fantom require you to manually create a class that joins a set of mixins into a concrete named type. With traits you could join a set of mixins at instance declaration time and/or maybe at variable type declaration time.
My initial thoughts is that although creating a proper named type with the mixins you want to use is annoying, it makes things simple and easy to understand. How much expressiveness do we gain versus complexity added?
DanielFathWed 7 Sep 2011
Well technically no. Dynamic behavior isn't a property of a trait. Neither is extending classes. Only trait like thing is doing -greet to remove a conflicting greet slot from it's sum (greet() still needs to match signatures and return types iirc).
Ok I'm gonna take an example. This the simplest example I can make. Here is the shared code:
mixin VolumeControl
{
abstract Int volume
Void incrVolume { return ++volume;}
Void decrVolume { return --volume;}
}
mixin ChannelControl
{
abstract Int channel
Void nextChannel { return ++channel;}
Void prevChannel { return --channel;}
}
class Radio
{
Int volume = 0
}
class TV
{
Int volume = 0
Int channel = 0
}
Currently:
class RadioWithVolume : Radio, VolumeControl {override Int volume;}
class TvWithVolumeNoProgram : TV, VolumeControl {override Int volume;}
class TvWithVolumeAndProgram : TV, VolumeControl {override Int volume; override Int channel;}
radioWithVolume := RadioWithVolume()
tvWithVolumeNoProgram := TvWithVolumeNoProgram()
tvWithVolumeAndProgram := TvWithVolumeAndProgram
//approximately 397 character
My proposal:
radioWithVolume := Radio & VolumeControl
tvWithVolumeNoProgram := TV & VolumeControl
tvWithVolumeAndProgram := TV & VolumeControl & ProgrammControl
//approximately 164 characters.
Dynamic mixin are used for some quick easy mixing. They are similar to closure in that aspect. If you use it a lot it should probably be an independent class/mixin (in case of a closure it should probably be an independent Func object). My suggestion is merely to enhance existing mixin not replace the way they are constructed.
Dynamic mixins selling point isn't expressiveness as much as it is flexibilty. Basically it allows for huge number of combination of mixins without need for class/mixin explosion.
andyThu 8 Sep 2011
Seems like a useful construct - only problem is that I'm not sure I've had a use case yet. But then again I haven't been paying attention either. I'll see how often it comes up in practice now.
DanielFath Tue 6 Sep 2011
Proposal
Ok, so I've been digging through Scala's traits and some interesting reading material and have this to report. First off Scala's traits aren't really traits. For trait to be a trait in accordance too Traits composable units of behavior[pdf] it has to:
Example:
When looking at Scala's trait the biggest beef I have with it is linearisation and state variables in traits. This means that class behavior depends on order in which they are called so
class A: Student, Worker
is not the same asclass B: Worker, Student
. Even somewhat bizarrely it is the rightmost trait that is most important.Analogous I propose we make mixins a bit more flexible and possible add some kind of keyword for their runtime combining.
Proposed syntax:
I propose we use
&
sign for adding dynamic. Why I chose the symbol? Brevity. I can be replaced with:
orwith
or something else.This compiles into Java as
Difference between today's mixin and the mixin I propose is somewhat superficial. "Dynamic" mixin would automatically recognize abstract field and override them while creating a default constructor if possible:
Possible issues: This might be dog slow. What I noticed Scala often does in times like this is make
VolumeControl : Radio
in order to staticaly get its field. It might be worth to do a couple of experiments to see what approach is best from a performance standpoint.Conflict resolution
Ok but how do we sort conflicts that arise once we have a collision? Let's go back to previous example and say we want to make:
Alternative conflict resolutions:
But previous example leaves a lot to be desired for. First it's a lot of boiler plate code, almost as bad as writing whole class. Let's say we don't want to print
I
m a Foo!' for FooBar.Alternatively we want it to print both "I'm Foo!\n I'm Bar!". In that case
Pros & Cons
Pro:
Cons:
I've concluded the dynamic mixin proposal. Here is my take on how we can use it.
Further thoughts
This is independent of the existing proposal. We might want to take a page from Scala's book and implement one rule that isn't specified in the above pdf. What I mean is that we allow for a trait to "extend" a class. What this means is simple:
Example:
However this has the problem of introducing of having to deal with one more level of slot collision. The problem can be solved in two ways:
Sortable : List
andImmutable : List
then definingSortableImmutable : Sort, Immutable
would cause a compiler error). Less complex and more constrained. Probably a better initial design.So in addition to standard rules of mixin composition shown earlier (e.g.
sort := Int[,] & Sortable & Immutable -add
) here are some ideas I had in mindThis proposal for extending classes would save us a lot boilerplate code but it is rather complex. It's an advanced feature I might elaborate upon later.
brian Wed 7 Sep 2011
Thanks for taking the time to write up such a complete proposal!
I think the fundamental idea here is that mixins as designed in Fantom require you to manually create a class that joins a set of mixins into a concrete named type. With traits you could join a set of mixins at instance declaration time and/or maybe at variable type declaration time.
My initial thoughts is that although creating a proper named type with the mixins you want to use is annoying, it makes things simple and easy to understand. How much expressiveness do we gain versus complexity added?
DanielFath Wed 7 Sep 2011
Well technically no. Dynamic behavior isn't a property of a trait. Neither is extending classes. Only trait like thing is doing
-greet
to remove a conflicting greet slot from it's sum (greet()
still needs to match signatures and return types iirc).Ok I'm gonna take an example. This the simplest example I can make. Here is the shared code:
Currently:
My proposal:
Dynamic mixin are used for some quick easy mixing. They are similar to closure in that aspect. If you use it a lot it should probably be an independent class/mixin (in case of a closure it should probably be an independent
Func
object). My suggestion is merely to enhance existing mixin not replace the way they are constructed.Dynamic mixins selling point isn't expressiveness as much as it is flexibilty. Basically it allows for huge number of combination of mixins without need for class/mixin explosion.
andy Thu 8 Sep 2011
Seems like a useful construct - only problem is that I'm not sure I've had a use case yet. But then again I haven't been paying attention either. I'll see how often it comes up in practice now.