Scala将类型的元组转换为类型的泛型元组



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

type a = (Int, String, Int)

并且我将通用类Foo定义为

class Foo[A]{}

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

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

虽然这在没有shapeless的原生scala或scala 2.*中类型级别上的宏的情况下是不可能的,但可以创建一个将类型a的值映射到类型b的值的函数。您应该做的一件事是定义类型类trait LiftFoo[T]它有一个单独的方法CCD_ 7,可以实现您想要的所有类型。

然后您可以使用polyfunction映射元组:

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}
}

然而,scala3(即Dotty(将允许在类型级别上使用类型lambdas和匹配类型来执行此操作。Typelambda允许形式为type LiftFoo[T] = [T] =>> Foo[T]的类型scala3中的匹配类型和元组现在重新汇编HList,您可以递归地遍历具有匹配类型的元素:

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

在Scala 3中,您可以简单地执行以下操作:

type b = Tuple.Map[a, Foo]

在价值层面上,你可以做例如:

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

如前所述,如果不使用一些复杂的类型级魔术(在本例中为Shapeless(,就无法在Scala2.x中实现这一点。我的实现使用了一个通用表示,并包含一些注释来标记一些辅助内容。

然而,为了完全理解通用表示是如何工作的,你必须至少阅读《无定形指南》的前几章。

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)
}

最新更新