为什么Scala不会在此类型类中选择最具体的实例?

时间:2015-05-05 19:37:06

标签: scala type-inference typeclass

我正在努力在Scala中构建这个计算管道构建器。我想要一个有两个方法的类mapreduce,它们在"流畅的界面中接收匿名函数"。这些函数将被组合,所以我想对它们进行类型检查,同时从前一个方法调用中推断出它们的输入类型...参见我的related question(它的所有部分都是难题)。

我的所有问题都过分简化了问题,但答案很有帮助,我想我差不多到了那里。

我已经设法使一切正常工作,只要我在注册具有KeyVal输出的mapper函数时使用的特殊方法。但是我想为函数使用相同的map名称,并且通常也要简化体系结构。为此,我决定尝试使用类型类模式。这允许我根据构建器方法的参数中的函数的类型执行不同的操作。请记住,我的问题的一部分是,如果我向mapper方法提供一个输出KeyVal[K,V]类型(几乎是一个元组)的函数,我需要存储这个{{1} }和K作为构建器类中的类型参数,因此可以在以后使用V方法对它们进行类型检查/推断。

这是我的构建器类

reducer

这就是在主程序中使用该类的方法

case class PipelineBuilder[A, V](commandSequence: List[MRBuildCommand]) {

  trait Foo[XA, XB, +XV] {
    def constructPB(xs: XA => XB): PipelineBuilder[XB, XV]
  }

  implicit def fooAny[XA, XB]: Foo[XA, XB, Nothing] = new Foo[XA, XB, Nothing] {
    def constructPB(ff: XA => XB) = PipelineBuilder[XB, Nothing](MapBuildCommand(ff) :: commandSequence)
  }

  implicit def fooKV[XA, XK, XV]: Foo[XA, KeyVal[XK,XV], XV] = new Foo[XA, KeyVal[XK,XV], XV] {
    def constructPB(ff: XA => KeyVal[XK,XV]) = PipelineBuilder[KeyVal[XK,XV], XV](MapBuildCommand(ff) :: commandSequence)
  }

  def innermymap[AA, FB, FV](ff: AA => FB)(implicit mapper: Foo[AA, FB, FV]) = mapper.constructPB(ff)

  def mymap[FB](ff: A => FB) = innermymap(ff)


  def rreduce[K](newFun: (V, V) => V)(implicit ev: KeyVal[K, V] =:= A) =
    PipelineBuilder[A,V](RedBuildCommand[K, V](newFun) :: commandSequence)

  def output(dest: MRWorker) = constructPipeline(dest)
  //...

}

请注意,object PipelineLab extends App { val mapredPipeline = PipelineBuilder[String, Nothing](List()) .mymap { s: String => s.toLowerCase } .mymap { s: String => KeyVal(s, 1) } .rreduce(_ + _) .output(OutputWorker) // ... } 不是必需的,因为如果类中的类型参数s: String。同样适用于A中的V

我已经设法在下面的简单示例中使用了类型类模式。如果我输出一个东西的元组,它会做一些不同的事情......就在这里。

rreduce

像魅力一样工作。但后来我无法使其适应我的实际问题......现在看来,编译器似乎没有寻找更具体的object TypeClassLab extends App { trait FuncAdapter[A, B] { def runfunc(x: A, f: A => B): B } implicit def myfunplain[X, A]: FuncAdapter[X, A] = new FuncAdapter[X, A] { def runfunc(x: X, f: X => A): A = { println("Function of something to plain, non-tuple type") f(x) } } implicit def myfuntuple[X, AA, AB]: FuncAdapter[X, (AA, AB)] = new FuncAdapter[X, (AA, AB)] { def runfunc(x: X, f: X => (AA, AB)): (AA, AB) = { println("Function from String to tuple") f(x) } } def ffuunn[A, B](x: A)(f: A => B)(implicit fa: FuncAdapter[A, B]) = { fa.runfunc(x, f) } println(ffuunn("obaoba") { s => s.length }) println(ffuunn("obaobaobaobaoba") { s => s.length }) println(ffuunn("obaoba") { s => (s.length, s.reverse) }) println(ffuunn("obaobaobaobaoba") { s => (s.length, s.reverse) }) } //OUTPUT: //Function of something to plain, non-tuple type //6 //Function of something to plain, non-tuple type //15 //Function from String to tuple //(6,aboabo) //Function from String to tuple //(15,aboaboaboaboabo) 隐式,而是总是选择fooKV,这会导致我尝试运行fooAny时出错,因为它期待rreduce。我如何使它工作?

1 个答案:

答案 0 :(得分:1)

我不确定我完全理解你的问题。

至于选择fooAny vs fooKV,必须知道Foo的实例,并从已知类型的网站中正确传递。这将是调用mymap的地方。但Foo不作为参数传递。

def mymap[FB](ff: A => FB) = innermymap(ff)

要求在调用innermymap(ff)时知道它。此时,类型信息丢失。唯一可用的Foo实例是fooAny

这实际上是为什么不应该存在像fooAny这样的定义的例子。您正在定义任何XA与任何XB之间的有效关系,即使这些关系实际上只是Any。此定义的存在导致您的代码在不应该的情况下键入check。这很可能会再次发生。