链接函数到泛型类的隐式转换



我有以下代码,它本应采用函数A => Boolean(输入类型上的泛型),并通过链式隐式转换将其转换为泛型特征Y[A]

val f: Int => Boolean = ???
trait X[A] {
def m1: Unit
}
implicit def toX[A](f: A => Boolean): X[A] = ???
f.m1  // Works
trait Y[A] {
def m2: Unit
}
implicit def toY[T, A](x: T)(implicit toX: T => X[A]): Y[A] = ???
f.m2  // Won't compile

不幸的是,最后一行无法编译。

执行以下任何一个更改就足以编译代码:

  • 转动X非通用
  • 转向Y非通用
  • 输出类型(Int => A)上的函数泛型替换源类型(它是输入类型上的泛型函数)
  • 用其他泛型类型(如Option[A]Seq[A]Array[A])替换源类型

基于此,我的结论是隐式转换链不起作用,因为源类型(输入类型上的函数泛型)是泛型和反变量中间类型和目标类型(X[A]Y[A])是泛型。

关于如何解决这个问题有什么想法吗?

UPDATE:最后的代码缩进,不仅可以将函数作为源类型处理,还可以处理其他类型(Option[A]Seq[A]A等)。为了实现这一点,我们的想法是使用toX函数的版本,将这些类型中的每一个转换为X[A]。则只需要toY的一个版本。

我想我有一个解决方案可以解决你的问题,请查看以下代码:

val f: Int => Boolean = _ => true
trait X[A] {
def m1: Unit
}
implicit def funcToX[A](f: A => Boolean): X[A] = new X[A] {
override def m1: Unit = println("Hello x")
}
f.m1  // Works
trait Y[A] {
def m2: Unit
}
implicit def toY[T[_,_], A](x: T[A, Boolean])(implicit toX: T[A, Boolean] => X[A]): Y[A] = new Y[A] {
override def m2: Unit = {
x.m1
println("Hello y")
}
}
f.m2 // now works

我在这里使用更高级的kinded类型语法。这是scala语言的高级功能,我没有足够的经验来正确解释它。对Y的转换应该适用于任何只需要两个类型参数的类型(并且已经定义了对X的转换)。

问题是,你真的需要转换为Y才能在参数T上通用吗?也许它接受函数作为参数就足够了:

implicit def toY[A](x: A => Boolean)(implicit toX: (A => Boolean) => X[A]) = ???

你可以阅读更多关于更高级的类型,例如这篇文章:

Scala:更高级的类型

更新

以下是作者提出的问题,我提出了以下解决方案:

type SingleGenericFun[T] = T => _
val f: SingleGenericFun[Int] = _ > 42
val g: Int = 42
trait X[A] {
def m1: Unit
}
implicit def toX(f: SingleGenericFun[Int]): X[Int] = ???
implicit def toX(x: Int): X[Int] = new X[Int] {
override def m1: Unit = println(x)
}
f.m1  // Works
trait Y[A] {
def m2: Unit
}
implicit def toY2[T[_, _], A](x: T[A, _])(implicit toX: T[A, Boolean] => X[A]): Y[A] = new Y[A] {
override def m2: Unit = {
x.m1
println("Hello y!")
}
}
implicit def toY0[A](x: A)(implicit toX: A => X[A]): Y[A] = new Y[A] {
override def m2: Unit = {
x.m1
println("Hello y!")
}
}
implicit def toY1[T[_], A](x: T[A])(implicit toX: T[A] => X[A]): Y[A] = new Y[A] {
override def m2: Unit = {
x.m1
println("Hello y")
}
}
g.m2
f.m2  // Compile

它仍然不是最好的解决方案,因为它需要提供3种(甚至更多)在技术上做同样事情的方法,我不知道如何将其通用化,也不知道它一开始是否可能。

最新更新