Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala for-comprehension type inference

The next code

  def f(chars: List[Char]): List[List[Char]] = chars match {
    case Nil => List(Nil)
    case x :: xs => for {
      v <- f(xs)
    } yield List(x) :: v
  }

gives the error message

- type mismatch;  found   : List[List[Any]]  required: List[List[Char]]

Please help me understand why 'for' chooses the most general Any instead of Char here? What topic in language spec should I read? Thanks.

like image 610
Observer Avatar asked May 10 '13 14:05

Observer


People also ask

Does Scala support type inference?

The Scala compiler can infer the types of expressions automatically from contextual information. Therefore, we need not declare the types explicitly. This feature is commonly referred to as type inference. It helps reduce the verbosity of our code, making it more concise and readable.

What would be the type inferred by Scala compiler for variable?

Scala compiler can automatically infer types of each variable declared. If the value of a variable is declared in double-quotes it will automatically be inferred as String. Also, the compiler can infer any value in a single quote is inferred as Char. The compiler, by default it infer any number without decimal as Int.

What is for comprehension in Scala?

Scala offers a lightweight notation for expressing sequence comprehensions. Comprehensions have the form for (enumerators) yield e , where enumerators refers to a semicolon-separated list of enumerators. An enumerator is either a generator which introduces new variables, or it is a filter.

What is the use of yield keyword in Scala for comprehension?

yield keyword will returns a result after completing of loop iterations. The for loop used buffer internally to store iterated result and when finishing all iterations it yields the ultimate result from that buffer.


2 Answers

The result, you are yielding is a mix of List[List[List[Char]]] and List[List[Char]]. Scala upcasts that to List[List[Any]]. For your case either of the following will do the job:

scala>  def f(chars: List[Char]): List[List[Char]] = chars match {
     |     case Nil => List(Nil)
     |     case x :: xs => for {
     |       v <- f(xs)
     |     } yield x :: v
     |   }
f: (chars: List[Char])List[List[Char]]

scala>  def f(chars: List[Char]): List[List[Char]] = chars match {
     |     case Nil => List(Nil)
     |     case x :: xs => for {
     |       v <- f(xs)
     |     } yield List(x) ++ v
     |   }
f: (chars: List[Char])List[List[Char]]
like image 162
George Avatar answered Sep 20 '22 08:09

George


The problem is List(x) -- it needs to be x.

First, v iterates over the results of f(xs), and f returns List[List[Char]]. That means the result will be List[X], where X is the type returned by yield.

The type of v is List[Char], since it is iterating over the contents of f(xs). So we have to figure out the type of List(x) :: v, which is prepending a List[Char] on a List[Char]. It is not concatenating them: it is adding a list to a list containing only characters. The resulting list will have both Char and List[Char] in it.

Since the only type that satisfy both is Any, then X will be Any and the result of the for-comprehension List[Any].

like image 6
Daniel C. Sobral Avatar answered Sep 18 '22 08:09

Daniel C. Sobral