Scala api design: can i avoid either unsafe casts or generics

I am writing a library that provides a distributed algorithm. The idea is that existing applications can add a library to use the algorithm. The algorithm is located in the library module, and it abstracts the actual transmission of data over the network below the line. An application using the algorithm must provide the actual network transport code. In code, it looks something like this:

// library is really a separate project not a single object
object Library {

  // handle to a remote server
  trait RemoteProcess

  // concrete server need to know how to actually send to a real remote process
  trait Server {
    def send(client: RemoteProcess, msg: String)
  }
}

// the application uses the library and provides the concrete transport
object AkkaDemoApplication {

  // concreate ref is a m wrapper to an actor ref in demo app
  case class ConcreteRemoteProcess(ref: akka.actor.ActorRef) extends Library.RemoteProcess

  class AkkaServer extends Library.Server {
    // **WARNING** this wont compile its here to make my question
    override def send(client: ConcreteRemoteProcess, msg: String): Unit = client.ref ! msg
  }
}

A few options that I have considered:

  • To have the AkkaServer method signature overload the library method, perform an insecure listing in ConcreteRemoteProcess. Yuk!
  • AkkaServer , RemoteProcesss ConcreteRemoteProcess. , , .
  • RemoteProcess.

3 :

object Library {

  trait Server[RemoteProcess] {
    def send(client: RemoteProcess, msg: String)
  }
}

object Application {
  class AkkaServer extends Library.Server[ActorRef] {
    override def send(client: ActorRef, msg: String): Unit = client ! msg
  }
}

3, , . , . , , . , , , .

, , , , 2 ( ) " , - ,

- Scala , " ", , ?

. , , , , . , . : , , , , API?

+4
1

, Akka. , , , ,

object Library {
  // handle to a remote server
  trait RemoteProcessInput

  trait RemoteProcessResult

  // concrete server need to know how to actually send to a real remote process and how to deal with the result
  trait Server {
    def handle(clientData: RemoteProcessInput) : Future[RemoteProcessResult] 
  }
}

Akka

object Application {
  class AkkaServerImpl(system: ActorSystem)
  extends Library.Server {
    override def handle(clientData: RemoteProcessInput)
  : ActorRef, msg: String): Future[RemoteProcessResult] = {
      // send data to client and expect result
      // you can distinguish target and msg from the concrete input
      val ref : ActorRef = ??? // (resolve client actor)
      val msg = ??? // (create your message based on concrete impl)
      val result = ref ? msg // using ask pattern here
                             // alternatively have an actor living on server side that sends msgs and receives the clients results, triggered by handle method
      result
    } 
}

}

+3

Source: https://habr.com/ru/post/1613899/


All Articles