Mikaël Mayer
Mikaël Mayer

Reputation: 10710

Scala type check

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)

Attempt #1 (failed)

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)

Attempt #2 (failed)

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?

Upvotes: 1

Views: 1277

Answers (2)

Alexey Romanov
Alexey Romanov

Reputation: 170909

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:

  1. 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)
    
  2. 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) }
    

Upvotes: 1

Mika&#235;l Mayer
Mika&#235;l Mayer

Reputation: 10710

The solution was to push the existential type inside.

object A {
  def fs: List[Test[A forSome { type A }]] = ???
  def test = for{f <- fs
    a <- f.get
    if f.check(a)} println(a)
}

And voilà it worked out.

Upvotes: 1

Related Questions