How to make this typecheck?
trait Test[A] {
type Output = A
def get: Iterable[A]
def check(a: A): Boolean
}
object A {
def fs: List[Test[_]] = ???
def test = for{f <- fs
a <- f.get
if f.check(a)} println(a)
}
It complains with
<console>:12: error: type mismatch;
found : a.type (with underlying type _$1)
required: _$1
if f.check(a)} println(a)
object A {
def fs: List[Test[_]] = ???
def test = for{f <- fs
a <- f.get
if f.check(a.asInstanceOf[f.Output])} println(a)
}
but then I have the same problem:
<console>:12: error: type mismatch;
found : f.Output
(which expands to) _$1
required: _$1
if f.check(a.asInstanceOf[f.Output])} println(a)
Having learned of some existential types, I am trying to replace the underscore -- at Test level, because not all A may be identical
object A {
def fs: List[Test[A] forSome { type A }] = ???
def test = for{f <- fs
a <- f.get
if f.check(a)} println(a)
}
It complains with:
<console>:12: error: type mismatch;
found : a.type (with underlying type A)
required: A
if f.check(a)} println(a)
How to get out of this type nightmare?
The basic issue is that the Scala compiler doesn't notice that the types hidden by the existential must be the same.
There are two solutions:
Make a single method calling both get
and check
:
object A {
private def getChecked[A](f: Test[A]) = f.get.withFilter(f.check)
def fs: List[Test[_]] = ???
def test = for { f <- fs; a <- getChecked(f) } println(a)
Give name to the existential using a type variable pattern. Unfortunately, this doesn't seem to work inside a for
, so you'll need to desugar it first:
// equivalent to your initial code
def test = fs.flatMap { f => f.get.withFilter { a => f.check(a) } }.
foreach { a => println(a) }
// with a type variable
def test = fs.flatMap { case f: Test[x] => f.get.withFilter { a => f.check(a) } }.
foreach { a => println(a) }