Scala练习中FoldLeft实现的sum3编译错误问题咨询
解决Scala自定义FoldLeft中sum3编译报错的问题
嘿,这个问题我之前练Scala泛型的时候也踩过坑,咱们一步步拆解清楚到底是怎么回事!
首先先还原你的代码场景(根据描述推测的实现,应该和你的版本差不多):
// 自定义的FoldLeft特质 trait FoldLeft[F[_]] { def foldLeft[A, B](fa: F[A], init: B)(f: (B, A) => B): B } // 给List实现FoldLeft实例 object FoldLeftInstances { implicit val listFoldLeft: FoldLeft[List] = new FoldLeft[List] { override def foldLeft[A, B](fa: List[A], init: B)(f: (B, A) => B): B = fa.foldLeft(init)(f) } } // 你的求和函数们 import FoldLeftInstances._ // 正常工作的sum1 def sum1[A](list: List[A])(implicit fl: FoldLeft[List], num: Numeric[A]): A = { fl.foldLeft(list, num.zero)(num.plus) } // 正常工作的sum2 def sum2[A: Numeric](list: List[A])(implicit fl: FoldLeft[List]): A = { val num = implicitly[Numeric[A]] fl.foldLeft(list, num.zero)(num.plus) } // 编译报错的sum3 def sum3(list: List[Int])(implicit fl: FoldLeft[List]): Int = { fl.foldLeft(list, 0)(_ + _) // 提示:Can not resolve reference + signature }
为什么sum3会报错?
问题出在类型推断的顺序和函数的隐式解析上:
- 对于sum1和sum2,我们用
Numeric[A].plus作为折叠函数,这个方法的类型是明确的(A, A) => A,Scala可以直接把它匹配到foldLeft需要的(B, A) => B(这里B和A是同一类型,因为初始值是num.zero,类型为A)。 - 但在sum3里,你用的是匿名函数
_ + _,Scala需要先推断出这个函数的参数类型才能解析+操作符。可foldLeft的类型参数A(对应List的元素类型Int)和B(对应初始值0的类型Int),与匿名函数的类型推断形成了循环依赖:- Scala需要知道
foldLeft的A和B类型,才能确定_ + _的参数类型; - 同时,Scala需要知道
_ + _的类型,才能确认它符合(B, A) => B的要求,进而确定A和B的类型。
- Scala需要知道
这种情况下,Scala的类型推断器没办法自动完成这个闭环,所以找不到+的正确签名。
怎么解决这个问题?
有几种简单的办法可以修复sum3:
方法1:显式指定匿名函数的参数类型
把_ + _换成显式的参数类型定义,打破类型推断的循环:
def sum3(list: List[Int])(implicit fl: FoldLeft[List]): Int = { fl.foldLeft(list, 0)((x: Int, y: Int) => x + y) }
方法2:显式指定foldLeft的类型参数
直接告诉ScalafoldLeft的A和B都是Int,这样它就能正确解析_ + _:
def sum3(list: List[Int])(implicit fl: FoldLeft[List]): Int = { fl.foldLeft[Int, Int](list, 0)(_ + _) }
方法3:复用Numeric的plus方法(和sum1/sum2保持一致)
既然sum1/sum2用Numeric能正常工作,sum3也可以这么做,还能保持代码风格统一:
def sum3(list: List[Int])(implicit fl: FoldLeft[List]): Int = { import Numeric.IntIsIntegral fl.foldLeft(list, IntIsIntegral.zero)(IntIsIntegral.plus) }
这样修改之后,sum3就能正常编译运行啦!
内容的提问来源于stack exchange,提问作者Yogesh Patil
相关产品推荐
相关产品推荐

