1

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 TransferObjects. Semantically important is:

  1. There are at least two kinds of TransferObjects.
  2. 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?

user573215
  • 4,679
  • 5
  • 22
  • 25

2 Answers2

1

you could use sealed trait TransferObject[T]{... and case class TransferObjectA(data: Int) extends TransferObject[Int] ... and inside of def performUseCase[T](transferObjects: Iterable[TransferObject[T]]) some

transferObject match{
case TransferObjectA(myInt) => ...
...
}//throws warning because of unmatched TransferObjectB on compile time because of sealed trait

also have a look at What is a sealed trait?

Community
  • 1
  • 1
Alex
  • 8,518
  • 4
  • 28
  • 40
0

Here's a type class-based approach:

trait TransferObject[T] {
  def data: T
}

case class TransferObjectA[T](data: T) extends TransferObject[T]
case class TransferObjectB[T](data: T) extends TransferObject[T]

trait Service {
  protected[this] trait Handler[A] {
    def apply(objs: Iterable[TransferObject[A]]): Unit
  }

  def performUseCase[A](objs: Iterable[TransferObject[A]])(
    implicit handler: Handler[A]
  ) = handler(objs)
}

And then:

object MyIntAndStringService extends Service {
  implicit object intHandler extends Handler[Int] {
    def apply(objs: Iterable[TransferObject[Int]]) {
      objs.foreach(obj => printf("Int: %d\n", obj.data))
    }
  }

  implicit object stringHandler extends Handler[String] {
    def apply(objs: Iterable[TransferObject[String]]) {
      objs.foreach(obj => printf("String: %s\n", obj.data))
    }
  }
}

val ints = List(1, 2, 3).map(TransferObjectA(_))
val strings = List("A", "B", "C").map(TransferObjectA(_))
val chars = List('a', 'b', 'c').map(TransferObjectA(_))

And finally:

scala> MyIntAndStringService.performUseCase(ints)
Int: 1
Int: 2
Int: 3

scala> MyIntAndStringService.performUseCase(strings)
String: A
String: B
String: C

scala> MyIntAndStringService.performUseCase(chars)
<console>:14: error: could not find implicit value for parameter handler: MyIntAndStringService.Handler[Char]
              MyIntAndStringService.performUseCase(chars)
                                                  ^

As desired.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680