zio-protoquill
zio-protoquill copied to clipboard
Arbitrary long Tuples (*:) produce Quat.Generic instead of Quat.Product
Version: 4.8.1
Module: quill-sql
Database: postgres
type MyRow1 = (Int, String)
type MyRow2 = Int *: String *: EmptyTuple
inline def q = quote{
querySchema[MyRow2]("my_table", t => t._1 -> "int_field", t => t._2 -> "string_field")
}
run(q)
Expected behavior
Both types should work fine.
Actual behavior
MyRow1
produces "SELECT x.int_field, x.string_field FROM my_table"
.
MyRow1
fails with io.getquill.quotation.QuatException: Was expecting SQL-level type must be a Product but found '<G>'
.
Steps to reproduce the behavior
https://scastie.scala-lang.org/i2RFvK3SS7SzIWwJW8fugw
Workaround
I'm currently using TupleConverter
:
type TupleConverter[T <: Tuple] =
T match
case EmptyTuple => EmptyTuple
case a *: EmptyTuple => Tuple1[a]
case a *: b *: EmptyTuple => (a, b)
case a *: b *: c *: EmptyTuple => (a, b, c)
case a *: b *: c *: d *: EmptyTuple => (a, b, c, d)
case a *: b *: c *: d *: e *: EmptyTuple => (a, b, c, d, e)
case a *: b *: c *: d *: e *: f *: EmptyTuple => (a, b, c, d, e, f)
This helps.
@getquill/maintainers
I found some strange behavior of Scala 3 inline of *:
. Here is a small example:
https://scastie.scala-lang.org/9GwHy3bOTsKDwd89bULfeA
Error message (super helpful, btw) contains detailed information about the tree that quill parser could not convert to Ast:
==== Tree cannot be parsed to 'Ast' ====
runtime.Tuples
.cons(1, Tuple_this)
.asInstanceOf[*:[Int, *:[String, EmptyTuple]]]
==== Extractors ===
TypeApply(
Select(
Apply(
Select(Select(Ident("runtime"), "Tuples"), "cons"),
List(Literal(IntConstant(1)), Ident("Tuple_this"))
),
"asInstanceOf"
),
List(Applied(TypeIdent("*:"), List(Inferred(), Inferred())))
)
...
It seems that the top level *:
has been inlined as Tuples.cons
according to it's definition:
inline def *: [H, This >: this.type <: Tuple] (x: H): H *: This =
runtime.Tuples.cons(x, this).asInstanceOf[H *: This]
The inner *:
is not present in the tree, though. It seems that it is represented as Tuple_this
(Ident("Tuple_this")
). As far as I can see, Tuple_this
might be a local val
, that had been created by compiler instead of this
. It's unclear, how to get it's definition to process by parser.
Upd
Looks like Scala creates a block with intermediate val
s:
{
val Tuple_this: scala.Tuple$package.EmptyTuple.type = scala.Tuple$package.EmptyTuple
val `Tuple_this₂`: scala.*:[java.lang.String, scala.Tuple$package.EmptyTuple] = (scala.runtime.Tuples.cons("", Tuple_this).asInstanceOf[scala.*:[java.lang.String, scala.Tuple$package.EmptyTuple.type]]: scala.*:[java.lang.String, scala.Tuple$package.EmptyTuple])
(scala.runtime.Tuples.cons(1, `Tuple_this₂`).asInstanceOf[scala.*:[scala.Int, scala.*:[java.lang.String, scala.Tuple$package.EmptyTuple]]]: scala.*:[scala.Int, scala.*:[java.lang.String, scala.Tuple$package.EmptyTuple]])
}