4
votes

I'm learning scala, and this question may be stupid, but... why?

For example, this is ok:

def matchList(ls: List[Int]): List[Int] = ls match {
  case 1 :: rest => rest
  case a :: b :: rest => (a + b) :: rest
  case _ => ls
}

matchList: (ls: List[Int])List[Int]

But function with type parameter does not compile:

def matchList[T](ls: List[T]): List[T] = ls match {
  case 1 :: rest => rest
  case a :: b :: rest => (a + b) :: rest
  case _ => ls
}

<console>:10: error: type mismatch;
found   : T
required: String
   case a :: b :: rest => (a + b) :: rest

Why?

4

4 Answers

8
votes

For any type T the operation T + T doesn't make any sense. (Do all types support +? No. Think of adding two dogs or two employees.)

In your case, the string concatenation operator is getting invoked (added via any2stringadd pimp), whose return type is (obviously) String. Hence the error message.

What you need is a way to specify that type T must support an operation where you combine two values of type T to yield a new value of type T. Scalaz's Semigroup fits the bill perfectly.

The following works:

def matchList[T : Semigroup](ls: List[T]): List[T] = ls match {
  case 1 :: rest => rest
  case a :: b :: rest => (a |+| b) :: rest // |+| is a generic 'combining' operator
  case _ => ls
}
4
votes

I think the problem lies in (a + b), the only universal usage of the + operator is string concatenation, so a and b must both be Strings (or automatically convertible to Strings) in order for that to be valid. Your parameterized type T isn't known to be a String, so it fails to compile.

2
votes

In the second example, your a, b variables of declared type T are not convertible to String, which is the required argument type of +, inferred from your program (i.e. the view applied to the type of arguments of +in the absence of any other information).

In the first example, inference can guess the right + function to apply, considering it takes as arguments the type of elements of the list, and that thankfully, you have mentioned in the type declaration that the type of those elements is Int. Try typing

"1"+2

1 + 2

... in an REPL and see what Scala tries to do. Then read about views.

Now, I surmise that by using that type parameter T above, you are trying to write a function that works with any numerical type, aren't you ? In that case, you can work with the Numeric trait. I'll let you read up on implicits before suggesting the following:

def matchList[T](ls: List[T])(implicit n:Numeric[T]): List[T] = {
  import n.mkNumericOps
  ls match {
    case 1 :: rest => rest
    case a :: b :: rest => (a + b) :: rest
    case _ => ls
}}

You get:

matchList(List(1,2,3))
res2: List[Int] = List(2, 3)
matchList(List(2,3,4))
res4: List[Int] = List(5, 4)
matchList(List(2.0,3.0,4.0))
res5: List[Double] = List(5.0, 4.0)
0
votes

Without any import:

def matchList[T](ls: List[T])(implicit wrapper:Numeric[T]): List[T] = ls match {
  case 1 :: rest => rest
  case a :: b :: rest => wrapper.plus(a, b) :: rest
  case _ => ls
}