I am designing an API. It basically looks like this:
trait Service {
def performUseCase[T](transferObjects: Iterable[TransferObject[T]])
}
trait TransferObject[T] {
def data: T
}
case class TransferObjectA[T](data: T) extends TransferObject[T]
case class TransferObjectB[T](data: T) extends TransferObject[T]
Note: If necessary I could change the implementation of the TransferObject
s. Semantically important is:
- There are at least two kinds of
TransferObject
s. - The transfer objects must build a sequential order in any way. It is not so important right now how that order is build: a
Seq[TransferObject[_]
, or one transfer object referencing the next or however.
There will be different implementations of the Service
trait. For each Service
instance it must be specified which types this instance can handle.
Example: This Service val myService = MyService()
can handle TransferObjects[MyTypeA]
and TransferObjects[MyTypeB]
etc.. When an API user tries to pass a TransferObjects[MyUnknownType]
the compiler should throw an error.
I read about Scala's type system, about type classes (e.g. with implicits) and type declarations, but I don't understand all details, yet.
I tried to use an implementation of the Service
trait with type specific handlers as type classes. MyHandler[MyTypeA]
, MyHandler[MyTypeB]
etc. and using implicits to pass in the correct handler for the current parameter type. But the handlers should not be exposed to the API user. That is why I wonder if it is possible at all, to throw compiler errors if the user passes in parameters of types that the Service
can't handle.
The real implementation is more complicated and currently broken. Maybe I can deliver some more code later.
Round-up:
So again: I want multiple Service instances. Each of them should be able to handle multiple parameter types. If a parameter of an unknown type is passed in, the compiler should throw an error. Type specific handlers are considered as implementation details and should remain hidden from API users.
How do I realize that?