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

Chaining implicit conversions of function to a generic class

我有以下代码,它应该采用函数 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[A]Y[A])是通用的。

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

更新: 最终代码缩进处理不仅是作为源类型的函数,也是其他类型(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

我在这里使用更高级的类型语法。它是 Scala 语言的高级功能,我还没有足够的经验来正确解释它。转换为 Y 应该适用于恰好采用两个类型参数(并且已定义转换 "toX")的任何类型。

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

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

您可以阅读更多关于更高种类的类型,例如这个 post:

Scala: Types of a higher kind

更新

根据问题作者的要求,我提出了以下解决方案:

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 种(或更多)技术上做同样事情的方法,我不知道如何泛化它,也不知道它是否可能在第一名.