Eli-Golin
10/26/2016 - 2:06 PM

Utilizing Object Orientation - Scala

Utilizing Object Orientation - Scala

Avoid abstract val in traits!

//The order in which thos two fields will be initialized is unknown!
trait Property {
  val name: String
  override val toString = """Property( $name )"""
}

The Property trait defines an abstract member 'name' which stores the current name of the property.
The 'toString' method is overriden to create a string using the 'name' member.

val x = new Property {override val name = "HI"} //java.lang.Object with Property(null)
The val x is defined as an instance of an anonymous subclass of the Property trait.
When creating this instance the REPL prints null instead of HI.
This is due to the order of initialization - the base trait Property is initialized
first during construction. Wheb the 'toString' method is looking for the value of 'name',
it hasn't been initialized yet. After taht the annonymous subclass is constructed and the 
value of 'name' is initialized.

Two ways to solve it:
1. Defining 'toString' as lazy - that means that only after the annonymous object 
   is fully constructed, the method is called. It doesn't garantee 
   the initialization order though 
2. Early member definition - is the other way (and prefered) for dealing with
    such problem.
    class X extends {val name = "HI"} with Property
    or
    new {val name = "HI"} with Property
 
 * For any complicated trait  hierarchies, early member initialization provide a more
   elegant solution to the problem
    
    
    
Provide empty implementations for abstract methods in traits!

Class linearization:
Linearization is the process of specifying a linear ordering to the superclass
of a given class.In Scala this ordering changes for each subclass and is 
reconstructed for classes in the hierarchy. This means that two subclasses of 
some common parent could have different linearization and therefore different 
behaviors.
1. Base class/trait are constructed first.
2. Traits are constructed left to right (because tait on the right is added later
   and thus overrides the prevoius traits)
3. To construct a trait it's base traits must be constructed first.
4. If a trait is constructed it is never reconstructed again
5. The construction order is the reverse of linearisation.

class A {
  def foo = "A"
}

trait B extends A {
  override def foo = "B" + super.foo
}

trait C extends B {
  override def foo = "C" + super.foo
}

trait D extends A {
  override def foo = "D" + super.foo
}

var x = new A with D with C with B //CBDA

We start building the linearisation according to the order of mixins left to right.

First A is constructed as it is the first base class!
D linearisation: 
  A not considered as already occured before.
  D extends A
C linearisation: 
  A not considered
  B extends A
  C extends B
C linearisation:
  A not considered
  B not considered
  
  ADBC (Whre A is constructed first and C constructed last)
  That's why the order of function executions is:
  C -> B -> D -> A

Sometimes we have several implementations of a certain function in different 
traits. and we would like to have the maximum flexability to choose which 
implementaion we're actually using.

Scala traits have a powerful ability to address some 'super' functionallity
without declaring explicitely their parent entity.
In order to achieve that we have to do one of two things:
1. Declare a self type on the trait. This approach limits how your traits
   might be mixed in.
   trait A {this: B => } //A can not ne mixed into a concrete class that does not
                         //also extends B
2. A better approach is to define an empty implementation on the entity 
   where traits may start being mixed in.
   trait A {
     //This is our default implementation, it doesn;t have to be empty.
     def foo = {}
   }
   
   trait B extends A {
    override def foo = {
       println("B")
       super.foo
     }
   }
   
   trait C extends A {
     override def foo = {
       println("C")
       super.foo
     }
   }
   
   trait D extends A {
     override def foo = {
       println("D")
       super.foo
     }
   }
   
   val x = new A with B with C with D //DCBA
   val y = new A with D with C with B//BCDA
   
* Ofcourse if our traits extend not just a base A trait
  the linearisation will be more complex.
   
When creating hierarchy of mixable behaviors via traits, we need to ensure the 
following:
 * We have a mixin point that traits can assume as a parent
 * The mixable traits delegate to their parent in a meaningfull way.
 * Provide a default implementation for hte chain-of-command style methods
   at your mixin point.