我一直坚持使用类型推断问题而且我不确定我是否做错了,编译器中存在错误,或者它是对语言的限制
我创建了一个虚拟示例来显示问题,用例没有意义,但请相信我,我有一个有效的用例
假设我有这个代码
val function: (Int, String) => String = (_, _) => ""
implicit class Function2Ops[P1, P2, R](f: (P1, P2) => R) {
def printArgs(p1: P1, p2: P2): Unit = println(p1, p2)
}
function.printArgs(1, "foo")
Run Code Online (Sandbox Code Playgroud)
这工作和打印(1,foo)
现在,如果我更改代码(注意副名称参数)
val function: (Int, => String) => String = (_, _) => ""
implicit class Function2Ops[P1, P2, R](f: (P1, P2) => R) {
def printArgs(p1: P1, p2: P2): Unit = println(p1, p2)
}
function.printArgs(1, "foo")
Run Code Online (Sandbox Code Playgroud)
它会打印出来 (1,MyTest$$Lambda$131/192881625@61d47554)
现在,在这一点上,我可以尝试模式匹配和/或使用TypeTag来提取值,以防万一是一个名字参数,但是,我实际上想要实现的是做这样的事情
trait Formatter[T] {
def format: String
}
case class ReverseStringFormat(v: String) extends Formatter[String] {
override def format: String = v.reverse
}
case class PlusFortyOneFormat(v: Int) extends Formatter[Int] {
override def format: String = (v + 41).toString
}
implicit def noOpFormatter[T](v: T): Formatter[T] = new Formatter[T] {
override def format: String = v.toString
}
val function: (Int, => String) => String = (_, _) => ""
implicit class Function2Ops[P1, P2, R](f: (P1, P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println( p1.format, p2.format)
}
function.printArgs(1, ReverseStringFormat("foo"))
Run Code Online (Sandbox Code Playgroud)
请注意,我的主要意图是我应该能够传递参数的原始类型或格式化器,这就是为什么这个扩展方法的签名使用的原因,这Formatter[TypeOfOriginalParam]也是implicit def noOpFormatter[T](v: T): Formatter[T]我不想要任何格式化的原因.
现在,在这里,我做不了多少,因为代码无法使用此错误进行编译
Error:(22, 40) type mismatch;
found : ReverseStringFormat
required: Formatter[=> String]
function.printArgs(1, ReverseStringFormat("foo"))
Run Code Online (Sandbox Code Playgroud)
如果我使用隐式类的名字创建第二个类型参数,我可以使它运行
val function: (Int, => String) => String = (_, _) => ""
implicit class Function2Ops[P1, P2, R](f: (P1, => P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println( p1.format, p2.format)
}
function.printArgs(1, ReverseStringFormat("foo"))
Run Code Online (Sandbox Code Playgroud)
这打印 (1,oof)
现在,主要问题是我想为任何函数执行此操作,无论其任何参数是按值还是按名称.那就是我被困住的地方,我可以为每个可能的副词组合创建不同的隐式类,其中有或不带名字参数但是它不实用,因为我需要为从Function1到Function10的每个函数执行此操作,并且by-name和by-value参数之间可能的组合数量将是巨大的.
有任何想法吗?如果我只对这种类型感兴趣,我真的需要关心论证的懒惰吗?我是否尝试做一些不受设计支持的事情,或者可能是编译器中的错误?
顺便说一句,这是我想要避免的
val function: (Int, => String) => String = (_, _) => ""
val function2: (Int, String) => String = (_, _) => ""
val function3: (=> Int, String) => String = (_, _) => ""
val function4: (=> Int, => String) => String = (_, _) => ""
implicit class Function2Ops[P1, P2, R](f: (P1, => P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println("f1", p1.format, p2.format)
}
implicit class Function2Opss[P1, P2, R](f: (P1, P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println("f2", p1.format, p2.format)
}
implicit class Function2Opsss[P1, P2, R](f: (=> P1, P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println("f3", p1.format, p2.format)
}
implicit class Function2Opssss[P1, P2, R](f: (=> P1, => P2) => R) {
def printArgs(p1: Formatter[P1], p2: Formatter[P2]): Unit = println("f4", p1.format, p2.format)
}
function.printArgs(1, "foo")
function2.printArgs(1, ReverseStringFormat("foo"))
function3.printArgs(1, "foo")
function4.printArgs(PlusFortyOneFormat(1), "foo")
Run Code Online (Sandbox Code Playgroud)
它的工作原理(注意我随机使用了格式化程序或原始值,如果原始参数是按名称或按值,则无关紧要)
(f1,1,foo)
(f2,1,oof)
(f3,1,foo)
(f4,42,foo)
Run Code Online (Sandbox Code Playgroud)
但是把这一切都写给我似乎有点奇怪
我建议使用类型类,以下是我将如何实现它们。
首先,我将创建一个用于打印单个参数的类型类。
trait PrintArg[A] { def printArg(a: A): String }
Run Code Online (Sandbox Code Playgroud)
然后我将实现用于处理按名称和按值参数类型的实例:
object PrintArg extends PrintArgImplicits {
def apply[A](implicit pa: PrintArg[A]): PrintArg[A] = pa
}
trait PrintArgImplicits extends PrintArgLowLevelImplicits {
implicit def printByName[A] = new PrintArg[=> A] { def printArg(a: => A) = a.toString }
}
trait PrintArgLowLevelImplicits {
implicit def printByValue[A] = new PrintArg[A] { def printArg(a: A) = a.toString }
}
Run Code Online (Sandbox Code Playgroud)
除了 Scala 禁止我们在函数声明语法之外的其他地方声明名称类型。
error: no by-name parameter type allowed here
Run Code Online (Sandbox Code Playgroud)
这就是为什么我们要解决这个问题:我们将在函数声明中声明名称类型,并将该函数提升到我们的类型类:
def instance[A](fun: A => String): PrintArg[A] = new PrintArg[A] { def printArg(a: A) = fun(a) }
def printByName[A] = {
val fun: (=> A) => String = _.toString
PrintArg.instance(fun)
}
def printByValue[A] = {
val fun: A => String = _.toString
PrintArg.instance(fun)
}
Run Code Online (Sandbox Code Playgroud)
现在,让我们将所有内容放在一起:
trait PrintArg[A] { def printArg(a: A): String }
object PrintArg extends PrintArgImplicits {
def apply[A](implicit pa: PrintArg[A]): PrintArg[A] = pa
def instance[A](fun: A => String): PrintArg[A] = new PrintArg[A] { def printArg(a: A) = fun(a) }
}
trait PrintArgImplicits extends PrintArgLowLevelImplicits {
implicit def printByName[A] = {
val fun: (=> A) => String = _.toString
PrintArg.instance(fun)
}
}
trait PrintArgLowLevelImplicits {
implicit def printByValue[A] = {
val fun: A => String = _.toString
PrintArg.instance(fun)
}
}
Run Code Online (Sandbox Code Playgroud)
最后,我们可以使用 Printer 中的类型类来一次处理所有情况:
implicit class Function2Ops[P1: PrintArg, P2: PrintArg, R](f: (P1, P2) => R) {
def printArgs(p1: P1, p2: P2): Unit =
println(PrintArg[P1].printArg(p1), PrintArg[P2].printArg(p2))
}
val function1: (Int, String) => String = (_, _) => ""
val function2: (Int, => String) => String = (_, _) => ""
function1.printArgs(1, "x")
function2.printArgs(2, "y")
Run Code Online (Sandbox Code Playgroud)
会打印
(1,x)
(2,y)
Run Code Online (Sandbox Code Playgroud)
奖励 1:如果您的类型不会打印任何有用的内容toString,那么您可以提供另一个隐式 def/val 并将支持扩展到所有 3x3 情况。
奖励 2:对于这个特定的示例,我基本上展示了如何实现和使用Show类型类,因此您可能需要在这里做的就是简单地重用现有的实现(并且可能只为按名称情况提供隐式 def)。但我将把它作为练习留给读者。
| 归档时间: |
|
| 查看次数: |
122 次 |
| 最近记录: |