Scala中的一个简单的foldRight类型问题

时间:2013-12-07 19:35:26

标签: scala

我正在做一个简单的练习来构建自己的List。我想定义一个将常规列表转换为MyList的方法。递归版似乎很好,但是当我尝试将其转换为foldRight时,我会得到一个诊断引用MyNonEmpty[T](x, ys),其中说:

- type mismatch; found : MyNonEmpty[T] required: MyEmpty[T]

我很感激帮助理解这一点。

abstract class MyList[T] {

  def listToMyList1(xs: List[T]): MyList[T] = xs match {
    case Nil => MyEmpty[T]
    case y :: ys => MyNonEmpty[T](y, listToMyList1(ys))
  }

  def listToMyList2(xs: List[T]): MyList[T] =
    xs.foldRight(MyEmpty[T])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))


case class MyEmpty[T] extends MyList[T] 

case class MyNonEmpty[T](val head: T, val tail: MyList[T]) extends MyList[T] 

2 个答案:

答案 0 :(得分:4)

问题的快速解决方案是,明确定义B的预期类型。

val empty:MyList[T] = MyEmpty[T]
xs.foldRight(empty)((x, ys) => MyNonEmpty[T](x, ys))

如果你正在深入解决问题,可以描述如下。

foldRight的签名如下。

def foldLeft[B](z: B)(f: (B, A) ⇒ B): B

此处type B被定义为方法类型参数。在Scala中,您无法在方法中定义的类型参数中定义类型方差。

def func[+A](a:A) = ... //Compiler error

作为第二点,如果你有多个参数列表,它的行为几乎与curried函数一样。

def func(a:Int)(b:Int) = ... // is almost equal to 
def func(a:Int) = (b:Int) => ...

因此,如果您填写第一个arg列表,它将创建另一个函数。在foldRight案例中,通过填写z,您也可以定义类型B。如果没有明确定义z的类型。具体类型设置为type B。当您尝试给函数f返回超类型B而不是B时,编译器会失败。

这是scala类型推断中的一种问题,但除了明确表示类型之外,没有其他可能的解决方案。

答案 1 :(得分:0)

错误是由于foldRight的定义:

def foldRight[B](z: B)(op: (A, B) ⇒ B): B

z的类型定义了返回类型,在您的情况下为MyEmpty。我看到两个选择:

 sealed trait MyList[T]

 case class MyEmpty[T] extends MyList[T] 

 case class MyNonEmpty[T](val head: T, val tail: MyList[T]) extends MyList[T] 

 object MyList {

   def listToMyList1(xs: List[T]): MyList[T] =
    xs.foldRight[MyList[T]](MyEmpty[T])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))

   def listToMyList2(xs: List[T]): MyList[T] =
    xs.foldRight(MyEmpty[T].asInstanceOf[MyList[T]])((x: T, ys: MyList[T]) => MyNonEmpty[T](x, ys))
 }

作为旁注:MyEmpty可以是单身:case object MyEmpty[Nothing],密封的特征协变:`密封特征MyList [+ T]。这样,您只有一个空列表实例,可以使用类型参数T的所有值。