3

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?

3 Answers 3

6

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)
}
Sign up to request clarification or add additional context in comments.

5 Comments

Are there pros and cons for each approach?
Yes, T <: HasId with HasCount accepts any type that implements HasId with HasCount. The other methods accept only type HasId with HasCount
@TerryDactyl I disagree. It accepts anything derived from them, as even the original code demonstrates - Foo is not hasId with hasCount, it only implements them, still all three solutions work with it.
scala> printData(Foo("a",2)) <console>:30: error: type mismatch; found : Foo required: hasId with hasCount printData(Foo("a",2))
@TerryDactyl Are you sure? I do not get his error (and I do not see why I should get it). Foo is hasId with hasCount, as it implements both of them.
2
def hasData[T <: HasId with HasCount](data: T): Unit = ???

5 Comments

Can you elaborate on your comment? Is this proper syntax?
T <: Whatever is a bit like saying "this generic type extends this other stuff". Then if you say "data is type T", you are saying that "data is a type which extends this other stuff"
I tried to type this in a scala worksheet. And I couldn't understand it.
This is correct syntax. <: is called an upper type bound. It means the function will accept instances of T that are subtypes of HasId with HasCount. This could be Apple with HasId with HasCount, Shark with HasId with Has Count or any other type that implements HasId with HasCount.
0

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])
                                  ^

7 Comments

Can you share with me why you think @TerryDactyl answer better?
You cannot call printData with an instance of Foo... You will get Type mismatch - found : Foo, required hasId with hasCount.
@Man-KitYau look at the following scastie
To explain why I like the generic way better, when calling printData without generics, as listed above will accept any instance that adheres to the contract i.e. is both an hasId and a hasCount. When you go the generic way then the upper bound is set to T. See the scastie in the comment
@Yaneeve I do not see a difference, unless you do something with T - e.g. return data back. Can you elaborate? To me it seems the same, it does not matter if you state the requirements as bounds, or as a type. I do not see how is the scastie you have linked related - decode and encode, no hasId / hasCount at all?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.