Man-Kit Yau
Man-Kit Yau

Reputation: 159

Scala, how to specify function parameter with multiple traits?

I have following piece of code, and I think my question will be apparent once you see it.

trait hasId {
  def id: String
}

trait hasCount {
  def count: Int
}

case class Foo(id: String, count: Int) extends hasId with hasCount

// This is the function I want to write
def printData(data: hasId and hasCount): Unit = {
  println(data.id + ": " data.count);
}

How should I do to declare the function signature?

Upvotes: 3

Views: 1525

Answers (3)

Yaneeve
Yaneeve

Reputation: 4779

I somehow like @TerryDactyl's answer better, but here is another way:

// This is the corrected function 
def printData(data: hasId with hasCount): Unit = {
 println(data.id + ": " + data.count)
}

EDIT:

Since the comments showed that I needed to explain and since the scastie link didn't work, following is the compiled code, with the compilation errors, I hope it helps:

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait hasId {
  def id: String
}

trait hasCount {
  def count: Int
}

trait Both extends hasId with hasCount

case class Foo(id: String, count: Int) extends Both
case class Bar(id: String, count: Int) extends hasId with hasCount
case class Baz(id: String, count: Int) extends Both



def printData(data: hasId with hasCount): Unit = {
 println(data.id + ": " + data.count)
}


def printDataT[T <: hasId with hasCount](data: T): Unit = {
 println(data.id + ": " + data.count)
}


val s = Seq(Foo("idFoo", 1), Bar("idBar", 2))

val fooss = Seq(Foo("idFoo", 1), Foo("idFoo2", 2))
val boths = Seq(Foo("idFoo", 1), Baz("idBaz2", 2))


s.foreach(printData)
fooss.foreach(printData)
boths.foreach(printData)

fooss.foreach(printDataT[Foo])
fooss.foreach(printDataT[Both])
boths.foreach(printDataT[Both])
boths.foreach(printDataT[Foo])

s.foreach(printDataT[hasId with hasCount])
s.foreach(printDataT[Both])
s.foreach(printDataT[Foo])




// Exiting paste mode, now interpreting.

<console>:54: error: type mismatch;
 found   : Foo => Unit
 required: Product with Serializable with hasId with hasCount => ?
              s.foreach(printDataT[Foo])
                                  ^
<console>:48: error: type mismatch;
 found   : Foo => Unit
 required: Both with Product with Serializable => ?
              boths.foreach(printDataT[Foo])
                                      ^
<console>:51: error: type mismatch;
 found   : Both => Unit
 required: Product with Serializable with hasId with hasCount => ?
              s.foreach(printDataT[Both])
                                  ^

Upvotes: 0

Terry Dactyl
Terry Dactyl

Reputation: 1868

def hasData[T <: HasId with HasCount](data: T): Unit = ???

Upvotes: 2

Suma
Suma

Reputation: 34453

The solution to the answer is with keyword, which is used in the meaning of your and. Generics as Terry Dactyl wrote are one possible solution:

def printData[T <: hasId with hasCount](data: T): Unit = {
  println(data.id + ": " + data.count)
}

Another is a type alias using with:

type hasIdAndCount = hasId with hasCount
def printData(data: hasIdAndCount): Unit = {
  println(data.id + ": " + data.count)
}

Or even directly:

def printData(data: hasId with hasCount): Unit = {
  println(data.id + ": " + data.count)
}

Upvotes: 6

Related Questions