【问题标题】:Scala convert tuple of types to tuple of generic of typesScala 将类型元组转换为泛型类型元组
【发布时间】:2020-06-04 14:47:21
【问题描述】:

假设我将类型 a 定义为类型的元组

type a = (Int, String, Int)

我将泛型类 Foo 定义为

class Foo[A]{}

有没有什么方法是 Scala(最好是原生 Scala)可以将 a 转换为 b,一个泛型类型的元组?

type b = (Foo[Int], Foo[String], Foo[Int])

【问题讨论】:

  • 元组是否总是有 3 个元素?
  • @KrzysztofAtłasik No.

标签: scala generics types


【解决方案1】:

虽然这对于没有shapeless 的本机scala 或scala 2.* 中类型级别的宏实际上是不可能的,但可以创建一个将类型a 的值映射到类型b 的值的函数。你应该做的一件事是定义类型类trait LiftFoo[T] 它有一个方法 def lift[T](t: T)(implicit instance: LiftFoo[T]): Foo[T] 并为您想要的所有类型进行实现。

然后你可以用多功能映射你的元组:

object Bla{
  import shapeless.syntax.std.tuple._
  import shapeless.syntax._
  import shapeless.poly._
  case class Foo[T](t: T)
  trait LiftFoo[T] {
    def liftImpl(t: T): Foo[T]
  }
  object LiftFoo {
    def lift[T](t: T)(implicit instance: LiftFoo[T]): Foo[T] = instance.liftImpl(t)
  }
  implicit val liftInt: LiftFoo[Int] = {Foo(_)}
  implicit val liftString: LiftFoo[String] = {Foo(_)}
  implicit val liftDouble: LiftFoo[Double] = {Foo(_)}

  val a: (Int, Int, String, Double) = ???
  object LiftFooPoly extends Poly1 {
    implicit def onLiftable[T: LiftFoo] = at[T](LiftFoo.lift[T])
  }
  val b = a.map{LiftFooPoly}
}

但是,scala 3(即 Dotty)将允许使用 type lambdasmatch types 在类型级别上执行此操作。 Typelambda 允许表单类型type LiftFoo[T] = [T] =>> Foo[T] scala 3 中的匹配类型和元组现在与 HList 相似,您可以使用 match types 递归遍历其元素:

  type TupleLiftFoo[Xs <: Tuple] <: Tuple = Xs match
    case Unit => Unit
    case x *: xs => LiftFoo[x] *: TupleLiftFoo[xs]

【讨论】:

    【解决方案2】:

    在 Scala 3 中,您可以这样做:

    type b = Tuple.Map[a, Foo]
    

    在价值层面上,您可以这样做:

    (1, "bar", 2) map { [T] => (x: T) => new Foo[T] }
    

    【讨论】:

    • new 不需要。
    【解决方案3】:

    如前所述,在 Scala 2.x 中,如果不使用一些复杂的类型级魔法(在本例中为 Shapeless),就无法做到这一点。我的实现使用通用表示,并包含一些 cmets 来标记一些辅助事物。

    不过,为了完全理解泛型表示的工作原理,您必须至少阅读 The Shapeless Guide 的前几章。

    object Demo extends App {
    
      import shapeless._
      import syntax.std.tuple._
      import poly._
    
      // Generic representation of the input type
      val tupleGen = Generic[(Int, String, Int)]
    
      // Generic representation of the output type
      val tupleFooGen = Generic[(Foo[Int], Foo[String], Foo[Int])]
    
      // The type we want to lift all tuple members into
      class Foo[A] {}
    
      // The instance of the input type
      val input = (42, "four two", 43)
    
      // The "natural transformation" thing
      object liftFoo extends (Id ~> Foo) {
        def apply[T](t: T): Foo[T] = new Foo[T] {}
      }
    
      // Having done all the prep work, the conversion is really easy
      val result: (Foo[Int], Foo[String], Foo[Int]) =
        tupleFooGen.from(tupleGen.to(input).map(liftFoo))
    
      println(result)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-16
      • 1970-01-01
      • 1970-01-01
      • 2021-12-01
      • 2012-02-25
      • 1970-01-01
      相关资源
      最近更新 更多