学习一些新的Scala 3 comppiletime操作,对Tuple
有点困惑(特别是在*:
和EmptyTuple
上使用类型匹配)
import scala.compiletime.*
imort cats.Show
transparent inline def showForTuple[T <: Tuple]: Show[T] =
inline erasedValue[T] match
case _: EmptyTuple => (new Show[EmptyTuple] {
override def show(n: EmptyTuple): String = ""
}).asInstanceOf[Show[T]]
case _: (t *: EmptyTuple) => (new Show[t *: EmptyTuple] {
val showHead = summonInline[Show[t]]
override def show(tup: t *: EmptyTuple): String = showHead.show(tup.head)
}).asInstanceOf[Show[T]]
case _: (t *: ts) => (new Show[t *: ts] {
val showHead = summonInline[Show[t]]
val showTail = showForTuple[ts]
override def show(tup: t *: ts): String =
showHead.show(tup.head) + ", " + showTail.show(tup.tail)
}).asInstanceOf[Show[T]]
这在Scala 3.2.2上可以正常工作:
showForTuple[Int *: String *: EmptyTuple].show((1, "hola mundo"))
val res2: String = 1, hola mundo
但在以下情况下失败:
showForTuple[(Int, String)].show((1, "hola mundo"))
java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
rs$line$28$$anon$1.show(Lscala/Product;)Ljava/lang/String; @16: invokevirtual
Reason:
Type 'scala/Product' (current frame, stack[2]) is not assignable to 'scala/Tuple2'
Current Frame:
bci: @16
flags: { }
locals: { 'rs$line$28$$anon$1', 'scala/Product', 'scala/Product' }
stack: { 'java/lang/StringBuilder', 'cats/Show', 'scala/Product' }
Bytecode:
0000000: bb00 2c59 122d b700 302a b600 322b 4d2c
0000010: b600 38b8 003e b800 42b9 0045 0200 b600
0000020: 4912 4bb6 0049 2ab6 004d 2b4e b200 522d
0000030: b600 55b6 0059 b900 4502 00b6 0049 b600
0000040: 5cb0
... 66 elided
从以下方面学习:https://docs.scala-lang.org/scala3/reference/metaprogramming/compiletime-ops.html和其他资源(博客文章/视频)
编辑:感谢Scala的Discord中的Il Totore,以下是一个解决方案,但对最初尝试时导致java.lang.VerifyError的原因感到困惑:
import scala.compiletime.*
import cats.Show
given Show[EmptyTuple] = _ => ""
lazy val given_Show_EmptyTuple: cats.Show[EmptyTuple]
given [A, T <: Tuple](using showA: Show[A], showT: Show[T]): Show[A *: T] =
_ match
case h *: EmptyTuple => showA.show(h)
case h *: t => showA.show(h) + ", " + showT.show(t)
transparent inline def showForTuple[T <: Tuple]: Show[T] =
inline erasedValue[T] match
case _: EmptyTuple => summonInline[Show[EmptyTuple]].asInstanceOf[Show[T]]
case _: (t *: EmptyTuple) => summonInline[Show[t *: EmptyTuple]].asInstanceOf[Show[T]]
case _: (t *: ts) => summonInline[Show[t *: ts]].asInstanceOf[Show[T]]
showForTuple[Int *: String *: EmptyTuple].show((1, "hola mundo")) // works
showForTuple[(Int, String)].show((1, "hola mundo")) // Also works?
1条答案
按热度按时间zrfyljdw1#
下面是使用内置方法
scala.compiletime.summonAll
和类型级别operationsTuple.Map
,Zip
等的另一个变通方法。我不得不使用
scala.compiletime.summonFrom
,因为编译器不知道Tuple.Union[Tuple.Map[T, [_] =>> A]] =:= A
。使用
mkString
而不是reduce
实现更简单下面的实现与您的类似,但使用的是match types。由于match类型不能嵌套,因此我将该方法拆分为两个
还有一个类似于您的实现,但使用
summonFrom
(在编译时失败)而不是asInstanceOf
(在运行时失败)或者
Scala 3. Implementing Dependent Function Type