2

This is cross-posted from the coursera functional programming course because there's a lot less activity on that forum.

I wrote the following code (parts are redacted because it's homework):

type Occurrences = List[(Char, Int)]
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
  val mx: Map[Char, Int] = x toMap
  y.foldLeft (redacted) (redacted => simple expression using updated and -)) toList
}

This produces the following compile error:

type mismatch; found : Map[Char,Int] required: <:<[(Char, Int), (?, ?)]

However if I add a copy of the third line, without the toList, in between via a val statement, the error goes away:

type Occurrences = List[(Char, Int)]
def subtract(x: Occurrences, y: Occurrences): Occurrences = {
  val mx: Map[Char, Int] = x toMap
  val foo: Map[Char, Int] = y.foldLeft (redacted) (redacted => simple expression using updated and -))
  y.foldLeft (redacted) (redacted => simple expression using updated and -)) toList
}

I'm guessing this has something to do with giving some kind of extra hint to the type checker, but does anyone know specifically why this happens?

4

2 回答 2

3

Below follows a few examples and some explanations on why it happens.

First, a working and a non-working cases:

scala> { List('a -> 1, 'b -> 2).toMap
     | println("aaa") }
aaa

scala> { List('a -> 1, 'b -> 2) toMap
     | println("aaa") }
<console>:9: error: type mismatch;
 found   : Unit
 required: <:<[(Symbol, Int),(?, ?)]
              println("aaa") }
                     ^

This happens because the syntax "obj method arg" is considered to be "obj.method(arg)" and so is "obj method \n arg", this way the argument can be written in the next line. Notice below:

scala> { val x = List('a -> 1, 'b -> 2) map 
     | identity
     | 
     | println(x) }
List(('a,1), ('b,2))

It's the same as List('a -> 1, 'b -> 2).map(identity).

Now for the weird error message found : Unit, required: <:<[(Symbol, Int),(?, ?)]. It happens that toMap actually takes one argument, here is it's signature:

def toMap[T, U](implicit ev: <:<[A,(T, U)]): Map[T,U],

but it's an implicit argument, so doesn't need to be provided explicitly in this case. But when you use the obj method \n arg syntax it fills the method argument. In the above non-working example the argument is println which has type Unit, hence it is not accepted by the compiler.

One workaround is to have two \n to separate the lines:

scala> { List('a -> 1, 'b -> 2) toMap
     | 
     | println("aaa") }
aaa

You can also use a ; to separate the lines.

于 2012-10-28T23:24:49.520 回答
2

@RexKerr & @DidierDupont are right, you're having issues because you called toMap like a binary operator, so the compiler freaked out.

My two cents: you should probably read the Suffix Notation section of the Scala Style Guide.

于 2012-10-28T23:15:36.167 回答