Scala really weird Type Mismatch

2020-04-21 03:58发布

问题:

I'm trying to implement dropWhile in Scala but I get a type mismatch, on the invocation of "f(h)" error that says it actually found the type it was expecting:

def dropWhile[A](l: XList[A])(f: A => Boolean): XList[A] = {

        def dropWhile[A](toCheck: XList[A], toKeep: XList[A]) : XList[A] = toCheck match {
            case XNil => toKeep
            case Cons(h, t) if **f(h)** == false => dropWhile(tail(toCheck), Cons(h, toKeep))
            case Cons(_, Cons(t1, t2)) => dropWhile(Cons(t1, t2), toKeep)
        }

        dropWhile(l, XList[A]())
    }

error message:

 found   : h.type (with underlying type A)
[error]  required: A

relevant code:

sealed trait XList[+A] {}
case object XNil extends XList[Nothing]
case class Cons[+A](head: A, tail: XList[A]) extends XList[A]

EDIT:

Here's a way to make it compile - but the winning answer is better and explains why as well.

def dropWhile[A](l: XList[A])(f: A => Boolean): XList[A] = {

        @tailrec
        def dropWhile[A](toCheck: XList[A], toKeep: XList[A], dropItem: A => Boolean): XList[A] = toCheck match {
            case Cons(h, XNil) if !dropItem(h) => Cons(h, toKeep)
            case Cons(h, XNil) if dropItem(h) => toKeep
            case Cons(h, t) if !dropItem(h) => dropWhile(t, Cons(h, toKeep), dropItem)
            case Cons(h, t) if dropItem(h) => dropWhile(t, toKeep, dropItem)
        }

        dropWhile(l, XList[A](), f)
    }

回答1:

You already have type parameter A on the original 'dropWhile' which is scoping the type of f. However, you then introduce a second type parameter on the inner def which shadows the outer definition of A and scopes the type of the XList. So the problem is that the A are not the same type! If you remove the shadowed type, it all works (few other changes made to get your code to compile):

def dropWhile[A](l: XList[A])(f: A => Boolean): XList[A] = {
        def dropWhile(toCheck: XList[A], toKeep: XList[A]) : XList[A] = toCheck match {
            case XNil => toKeep
            case Cons(h, t) if f(h) == false => dropWhile(t, Cons(h, toKeep))
            case Cons(_, Cons(t1, t2)) => dropWhile(Cons(t1, t2), toKeep)
        }
        dropWhile(l, XNil)
    }


回答2:

You could just use a foldRight instead (I've simplified this to use List rather than XList):

scala> def dropWhile[A](l: List[A])(f: A => Boolean): List[A] =
     | l.foldRight(List[A]())((h,t) => if (f(h)) t else h :: t)
dropWhile: [A](l: List[A])(f: A => Boolean)List[A]

scala> val l = List(1,2,3,4,5,6,5,4,3,2,1)
l: List[Int] = List(1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1)

scala> l.dropWhile(_ < 4)
res1: List[Int] = List(4, 5, 6, 5, 4, 3, 2, 1)

Doesn't answer your found : h.type (with underlying type A) question though :-)



标签: scala