scalafunctional-programmingtraitsself-type

Scala abstract types implement trait that extends from generic trait


I am new to scala and thus my question might be due to a lack of understanding of abtract types and traits.

I currently have a Sensor trait which is generic and defines a value and newValue method.

trait Sensor[T] {
  def value: T
  def newValue(): Unit = {}
}

One concrete implementation of Sensor is MemorySensor, which just uses a variable to store the value and has a set value method which sets the value and fires the newValue method.

class MemorySensor[T] extends Sensor[T] {
  var internalValue: T = null.asInstanceOf[T]

  def setValue(newVal: T): Unit = {
    internalValue = newVal
    newValue()
  }

  def value: T = internalValue

}

There is also an AbstractSO (SO = subject + Observer) class which uses abstract types to implement Subject/Observer pairs.

class AbstractSO {
  type O <: AbstractObserver
  type S <: AbstractSubject

  trait AbstractSubject {
    this: S =>
    def register(observer: O) = {
      //we could use mutable collection here too
      observers = observer :: observers
    }

    var observers: List[O] = List[O]()

    def notifyObservers() = {
      observers.foreach(o => o.notifyObserver(this))
    }

  }

  trait AbstractObserver {

    def notifyObserver(subject: S)

    }

}

One example of a concrete Subject/Observer is the ActionSO

object ActionSO extends AbstractSO {

  type S = ActionSubject
  type O = ActionObserver

  trait ActionSubject extends AbstractSubject {
    def action() = {
      notifyObservers()
    }
  }

  trait ActionObserver extends AbstractObserver {
    override def notifyObserver(actionSubject: ActionSubject) = {
      println("action observer called")
    }
  }

}

Now I want to implement a concrete Subject/Observer-Pair for Sensors with the requirement that the SensorSubject should be a mixin trait for sensors.

So I guess the target would be to use the SensorSubject like this:

val x = new MemorySensor[Int] with SensorSubject[Int]

However whatever I try to implement the SensorSO, I always get either some kind of "illegal inheritance" error or "self-type does not conform to..".

As far as I know this cannot be done without creating an extra class that extends from AbstractSO, but uses generic types. (but I don´t know how this helps me to achieve my target anyways)

It would be very nice if someone could help me out!

EDIT:

As SergGr wanted to see my SensorSubject (which is what I don´t know how to implement, I will post one of my various tries)

Note however that this does NOT COMPILE

object SensorSO extends AbstractSensorSO {

  //TODO: i shouldn´t use "Any" here - this won´t work
  override type S  = SensorSubject[Any]

  trait SensorSubject[T] extends AbstractSensorSubject with Sensor[T] {
    this: S => //this generates problems as well
  }
}

Here is my AbstractSensorSO

class AbstractSensorSO extends AbstractSO {

  type S <: AbstractSensorSubject
  type O <: AbstractSensorObserver

  trait AbstractSensorSubject extends AbstractSubject  {
    this: S =>


  }

  trait AbstractSensorObserver extends AbstractObserver {

  }
}

As you can see the AbstractSensorSO basically doesn´t do anything, I just added it because it was mentioned in a hint to the solution that one needs an subclass of AbstractSO, before creating the concrete SensorSO object.

One problem I am facing is that the Sensor trait is generic so for the SensorSubject to use the Sensor trait AFAIK i have to make the SensorSubject generic too. Normally this wouldn´t be a problem, but as i use abstract types I would have to define the "S" type in the SensorSO with generics too (e.g.

type S = SensorSubject[T]

But as the generic type "T" is not known in that context it obviously gives an error (as the generic "T" is only available the context of the generic trait SensorSubject) If I try to drop the generic paramater when defining the type S, I also get an error message that the generic type parameter is missing. And just writing

type S = SensorSubject[Any]

doesn´t solve the problem either

EDIT2:

To clarify what my target is:

SensorSubject should be an Mixin Trait, such that i can use normal Sensors(not only MemorySensors) and that if I want I can add "with SensorSubject[Int]" to the creation of the Sensor and then it functions as a SensorSubject

Which means i can register an observer and the observer is notified when i change the value of the Sensors(that now functions as SensorSubject)

Here is an example how I would like to use the SensorSubject[T] Trait:

//creating a sensor WITH the SensorSubject Trait 
val sensorWithSubject= new MemorySensor[Int] with SensorSubject[Int]
sensorWithSubject.registerObserver(..)

//creating a normal Sensor WITHOUT SensorSubject 
val normalMemSensor = new MemorySensor[Int]

Solution

  • You didn't provide any example of expected usage so my guess might be wrong. Still here is my attempt:

    trait Sensor[T] {
      def value: T
    
      def newValue(): Unit = {}
    }
    
    class MemorySensor[T] extends Sensor[T] {
      var internalValue: T = null.asInstanceOf[T]
    
      def setValue(newVal: T): Unit = {
        internalValue = newVal
        newValue()
      }
    
      def value: T = internalValue
    }
    
    //////////////////////////////////
    
    
    trait AbstractSubject[S <: AbstractSubject[S, O], O <: AbstractObserver[S, O]] {
      this: S =>
      def register(observer: O) = {
        //we could use mutable collection here too
        observers = observer :: observers
      }
    
      private var observers: List[O] = List[O]()
    
      def notifyObservers() = {
        observers.foreach(o => o.notifyObserver(this))
      }
    
    }
    
    trait AbstractObserver[S <: AbstractSubject[S, O], O <: AbstractObserver[S, O]] {
      def notifyObserver(subject: S)
    }
    
    //////////////////////////////////
    
    trait SensorSubject[T, S <: SensorSubject[T, S, O], O <: SensorObserver[T, S, O]] extends Sensor[T] with AbstractSubject[S, O] {
      this: S =>
    }
    
    trait SensorObserver[T, S <: SensorSubject[T, S, O], O <: SensorObserver[T, S, O]] extends AbstractObserver[S, O]
    
    //////////////////////////////////
    
    class MemorySensorSubject[T] extends MemorySensor[T] with AbstractSubject[MemorySensorSubject[T], MemorySensorObserver[T]] {
      override def setValue(newVal: T): Unit = {
        super.setValue(newVal)
        notifyObservers()
      }
    }
    
    trait MemorySensorObserver[T] extends AbstractObserver[MemorySensorSubject[T], MemorySensorObserver[T]]
    

    and with that you can do

    def test(): Unit = {
      val sensor = new MemorySensorSubject[Int]
      val observer = new MemorySensorObserver[Int] {
        override def notifyObserver(subject: MemorySensorSubject[Int]): Unit = {
          println(s"New value of $subject is ${subject.value}")
        }
      }
      sensor.register(observer)
      sensor.setValue(42)
    }
    

    and the output will be

    New value of so.Main$MemorySensorSubject@363ee3a2 is 42

    Probably the most important thing here is that MemorySensorSubject is an explicitly named type that thus can be used as S in F-bound generic constraint