Search code examples
scalaabstract-type

Scala class check at runtime


I have this use case i cannot solve. I thought of an environment for message passing programming. There are two main concepts, Things and Environment: Things are like real world Things they can be passive or active and they can send and receive messages. The environment enables the communication between Things. I came up with this solution:

/**** Thing.scala ****/

abstract class Thing(environment : Environment){
  val uniqueName : String
  require(environment != null)
}

/**** ActiveThing.scala ****/

trait ActiveThing extends Thing {
  environment.register(this)
  type inputType
  type outputType
  def receiveMessage(message : Message[inputType]) : outputType
  def canReceiveMessage(subject : String) : Boolean
}    

/**** Environment.scala ****/

trait Environment {
  private var _passiveThings = List[PassiveThing]()
  private var _activeThings = List[ActiveThing]()

  def activeThings = _activeThings.toSeq
  def passiveThings = _passiveThings.toSeq
  def register(p : PassiveThing) = _passiveThings +:= p
  def register(a : ActiveThing)  = _activeThings +:= a
  def unicastRequest[T,K](from : String, message : Message[T], to : String) : K
}

/**** Message.scala ****/

case class Message[T](subject : String, body : T)

But when i try to actually implement:

/**** EchoActiveThing.scala ****/

class EchoActiveThing(implicit inj: Injector) extends Thing()(inj) with ActiveThing {
  type inputType = String
  type outputType = String
  val uniqueName : String = "Echo"
  def receiveMessage(message : Message[inputType]) : outputType = {
    message.body
  }
  def canReceiveMessage(subject : String) : Boolean = {
    true
  }
}

/**** BasicEnvironment.scala ****/

class BasicEnvironment extends Environment {
  def unicastRequest[T,K](from : String, message : Message[T], to : String) : K = {
     activeThings.filter{ x => 
       x.inputType == T &&
       x.outputType == K &&
       x.canReceiveMessage(message) &&
       activeThings.contains(to.uniqueName)
     }
   }
}

But it doensn't compile. I think I'm not approaching the problem in the right way! Thank you


Solution

  • x.inputType is a type projection, not a variable. It cannot be compared with == and that's why code does not compile. To compare types in runtime you can use TypeTag[T]

    case class Holder[T](t: T)
    
    def areEqualTypes[A, B](a: Holder[A], b: Holder[B])
                           (implicit tagA: TypeTag[A], tagB: TypeTag[B]) = {
      tagA == tagB
    }
    
    val (h1, h2, h3) = (Holder("Foo"), Holder("Bar"), Holder(5))
    println(areEqualTypes(h1, h1)) //true
    println(areEqualTypes(h1, h2)) //true
    println(areEqualTypes(h1, h3)) //false
    

    In the example type tags are implicitly passed alongside with function parameters. This time you can use == because tagA and tagB are instances of TypeTag[A] and TypeTag[B] classes.