【问题标题】:Chaining implicit conversions of function to a generic class将函数的隐式转换链接到泛型类
【发布时间】:2017-06-05 22:07:45
【问题描述】:

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

关于如何解决这个问题的任何想法?

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

【问题讨论】:

    标签: scala generics implicit-conversion scala-implicits


    【解决方案1】:

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

    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 语言的高级功能,我没有足够的经验来正确解释它。转换 toY 应该适用于恰好采用两个类型参数的任何类型(并且已定义转换“toX”)。

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

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

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

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

    【讨论】:

    • 我的实际实现已经使用了更高种类的类型,但使用的方式非常不同。我发布的代码只是试图隔离问题的精简版本。最终代码不仅会处理函数,还会处理其他源类型(Option[A]Seq[A]A 本身等)。这些类型中的每一个都将具有*toX 的版本,但是如果将类型转换为X[A],则只需要toY 的一个版本。所以你的解决方案将不起作用。 :-(
    • 我会尝试将这些要求放在我的问题上。
    猜你喜欢
    • 2018-12-08
    • 1970-01-01
    • 2013-11-15
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多