Reputation: 3033
Why is this snippet not printing out: "You successfully implemented the function"
Details:
Why is the type of val actual seeming to be of type List[Either[List[Int], Int]], when I am even saying to the compiler explicitly that actual's type should be of type List[Int]?
// Flatten a nested list structure
def flatten[T](list: List[Either[List[T], T]]): List[T] = list flatMap {
// TODO: Implement
case list: List[T] => list
case element: T => List(element)
}
implicit def ElementToEitherLeft[T](obj: T) = Left(obj)
implicit def ElementToEitherRight[T](obj: T) = Right(obj)
val list: List[Either[List[Int], Int]] = List(List(1, 1), 2, List(3, 5))
val actual: List[Int] = flatten[Int](list)
val expected = List(1, 1, 2, 3, 5)
if (actual == expected) print("You successfully implemented the function")
else print("Unfortunatly, that's not quite rigth yet")
Upvotes: 1
Views: 248
Reputation: 39587
If you compile with 2.10, you will see a warning like:
<console>:7: warning: match may not be exhaustive.
It would fail on the following inputs: Left(_), Right(_)
If you compile with 2.10-M6, you will also see a spurious warning like:
warning: unreachable code
case element: T => List(element)
What you would like to see is a warning like:
warning: unreachable code
case list: List[T] => list
so you could realize your misapprehension about what's going on in the function you're giving to flatMap, i.e. oh right it's a List[Either] not a List[List], but this is the compiler doing its best at the moment.
These are the warnings that are turned off under -Xno-patmat-analysis
. There doesn't seem to be an option to turn it up to eleven.
You can eliminate your erasure warnings by getting rid of the type param, which is the natural thing to do, and then you get no support from the type system and no reachability or match warning:
def flatten(list: List[Either[List[_], _]]): List[Any] = list flatMap {
case list: List[_] => list
case element => List(element)
}
Upvotes: 1
Reputation: 139058
When you compiled your flatten
you should have seen a warning like this:
warning: there were 2 unchecked warnings; re-run with -unchecked for details
If you'd then compiled with with -unchecked
, you'd have seen this:
<console>:9: warning: non variable type-argument T in type pattern List[T] is unchecked since it is eliminated by erasure
case list: List[T] => list
^
<console>:10: warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure
case element: T => List(element)
In short, flatMap
isn't going to unwrap your Either
items for you, and what you've written only compiles because of some unpleasant facts about type erasure and pattern matching.
Fortunately there's an easy fix:
def flatten[T](list: List[Either[List[T], T]]): List[T] = list flatMap {
case Left(list) => list
case Right(item) => item :: Nil
}
Or, even better:
def flatten[T](list: List[Either[List[T], T]]): List[T] =
list.flatMap(_.fold(identity, List(_)))
Either will work as you expect.
Upvotes: 7