为什么 Scala 不选择这种类型 class 中最具体的实例?
Why isn't Scala picking the most specific instance in this this type class?
我正在努力用 Scala 构建这个计算管道构建器。我想要一个 class,它有两个方法,map
和 reduce
,它们在 "fluent interface" 中接收匿名函数。这些函数将被组合,所以我想对所有这些函数进行类型检查,同时从之前的方法调用中推断出它们的输入类型...请参阅我的 related question(这都是难题的一部分)。
我所有的问题都把问题简单化了,但答案很有帮助,我想我快到了。
只要我拥有在注册具有 KeyVal
输出的映射器函数时使用的特殊方法,我就设法使一切正常工作。但我想对函数使用相同的 map
名称,并从总体上简化架构。为此,我决定尝试使用 type class 模式。这允许我根据构建器方法参数中函数的类型做不同的事情。还要记住,我的部分问题是,如果我给 mapper
方法一个输出 KeyVal[K,V]
类型(几乎是一个元组)的函数,我需要存储这个 K
和 V
作为我的构建器 class 的类型参数,因此它们可用于稍后从 reducer
方法进行类型检查/推断类型。
这是我的建造者class
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)
//...
}
这就是class在主程序中的使用方式
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
来自 class。 rreduce
.
中的 V
也是如此
我已经设法在下面的简单示例中使用类型 class 模式。如果我输出一些东西的元组,它会做一些不同的事情......在这里。
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
。我如何让它发挥作用?
我不确定我是否完全理解你的问题。
就选择 fooAny
与 fooKV
而言,必须知道 Foo
的实例并从已知类型的站点适当地传递。这将是调用 mymap
的地方。 Foo
虽然没有作为参数传递。
def mymap[FB](ff: A => FB) = innermymap(ff)
你是要求它知道什么时候调用innermymap(ff)
。此时,类型信息丢失。 Foo
的唯一可用实例是 fooAny
.
这实际上是为什么像 fooAny
这样的定义不应该存在的一个例子。您正在定义任何 XA
和任何 XB
之间的有效关系,即使它们实际上只是 Any
。此定义的存在导致您的代码在不应该进行类型检查时进行类型检查。这很可能会再次发生。
我正在努力用 Scala 构建这个计算管道构建器。我想要一个 class,它有两个方法,map
和 reduce
,它们在 "fluent interface" 中接收匿名函数。这些函数将被组合,所以我想对所有这些函数进行类型检查,同时从之前的方法调用中推断出它们的输入类型...请参阅我的 related question(这都是难题的一部分)。
我所有的问题都把问题简单化了,但答案很有帮助,我想我快到了。
只要我拥有在注册具有 KeyVal
输出的映射器函数时使用的特殊方法,我就设法使一切正常工作。但我想对函数使用相同的 map
名称,并从总体上简化架构。为此,我决定尝试使用 type class 模式。这允许我根据构建器方法参数中函数的类型做不同的事情。还要记住,我的部分问题是,如果我给 mapper
方法一个输出 KeyVal[K,V]
类型(几乎是一个元组)的函数,我需要存储这个 K
和 V
作为我的构建器 class 的类型参数,因此它们可用于稍后从 reducer
方法进行类型检查/推断类型。
这是我的建造者class
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)
//...
}
这就是class在主程序中的使用方式
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
来自 class。 rreduce
.
V
也是如此
我已经设法在下面的简单示例中使用类型 class 模式。如果我输出一些东西的元组,它会做一些不同的事情......在这里。
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
。我如何让它发挥作用?
我不确定我是否完全理解你的问题。
就选择 fooAny
与 fooKV
而言,必须知道 Foo
的实例并从已知类型的站点适当地传递。这将是调用 mymap
的地方。 Foo
虽然没有作为参数传递。
def mymap[FB](ff: A => FB) = innermymap(ff)
你是要求它知道什么时候调用innermymap(ff)
。此时,类型信息丢失。 Foo
的唯一可用实例是 fooAny
.
这实际上是为什么像 fooAny
这样的定义不应该存在的一个例子。您正在定义任何 XA
和任何 XB
之间的有效关系,即使它们实际上只是 Any
。此定义的存在导致您的代码在不应该进行类型检查时进行类型检查。这很可能会再次发生。