【问题标题】:Why isn't Scala picking the most specific instance in this this type class?为什么 Scala 不选择这个类型类中最具体的实例?
【发布时间】:2015-05-05 19:37:06
【问题描述】:

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

我所有的问题都把问题简单化了,但答案很有帮助,我想我快到了。

只要我有注册具有KeyVal 输出的映射器函数时使用的特殊方法,我就设法使一切正常工作。但我想为这些函数使用相同的 map 名称,并总体上简化架构。为此,我决定尝试使用类型类模式。这允许我根据构建器方法参数中的函数类型做不同的事情。请记住,我的问题的一部分是,如果我给mapper 方法提供一个输出KeyVal[K,V] 类型(几乎是一个元组)的函数,我需要将这个KV 存储为来自我的构建器类的类型参数,因此它们可用于稍后从 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 来自类。 rreduce 中的 V 也是如此。

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

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,因为它期待V <: Nothing。如何让它发挥作用?

【问题讨论】:

    标签: scala type-inference typeclass


    【解决方案1】:

    我不确定我是否完全理解您的问题。

    就选择fooAnyfooKV 而言,Foo 的实例必须是已知的,并且必须从已知类型的站点适当地传递。这将是调用mymap 的地方。 Foo 虽然没有作为参数传递。

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

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

    这实际上是一个示例,说明为什么不应该存在像 fooAny 这样的定义。您正在定义任何XA 和任何XB 之间的有效关系,即使它们实际上只是Any。此定义的存在导致您的代码在不应该进行类型检查时进行类型检查。这很可能会再次发生。

    【讨论】:

    • 这很有意义。类型是否因擦除而不可用?那么我应该将整个类型类移动到主程序中,还是可以通过使用TypeTags 或其他方式将其留在PipelineBuilder 中?
    • 不幸的是,scala 的推断无法确定由调用者的泛型参数定义的泛型参数的类型。 IE。泛型推理只对类型类有效一层
    • @dividebyzero 同样,我不完全理解您要做什么,但是如果您想专门选择 fooKV,则需要将 mymap 替换为 innermymap . innermymap 签名中Foo 的附加类型参数和隐式参数需要在构建管道时可用,即特定类型已知的调用站点。你假装它们不存在然后要求它们。尝试解决此问题时,您应该做的第一件事是删除 fooAny 定义。
    • 好的,现在我在正确的范围内导入implicits,我认为我在正确的轨道上。一个有趣的细节是,现在我必须将对象作为参数发送到适配器的 constructPB 方法,但我认为没有更好的解决方案......谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-23
    相关资源
    最近更新 更多