Попробуйте написать общий макрос для десериализации кейс-классов, используя uPickle read в Scala 3:
inline def parseJson[T:Type](x: Expr[String])(using Quotes): Either[String, Expr[T]] = '{
try
Right(read[T]($x.toString))
catch
case e: Throwable => Left(s"Deserialization error: ${e.getMessage}")
}
получить ошибку:
Right(read[T]($x.toString))
^^^^^^^^^^^^^^^^^^^^
missing argument for parameter evidence$3 of method read in trait Api: (implicit evidence$3: upickle.default.Reader[T]): T
Я использую чтение uPickle для нескольких классов случаев, есть ли решение с меньшим количеством шаблонного кода? Спасибо за любую помощь!
Вам нужно:
Expr
- вы можете развернуть макрос внутри inline def
(точно так же, как макросы вампиров из Scala 2), но НЕ когда вы создаете выражения с помощью '{}
, тогда вам необходимо разрешить все неявные выраженияExpr[Either[Throwable, T]]
— при создании тела макроса Scala 3 все возвращаемое значение должно быть Expr
, иначе вы не сможете вывести его из кавычек внутри ${}
(оно может работать как значение, возвращаемое помощником, который превратит его в Expr
, но ${}
принимает только один вызов чего-то, определенного в области верхнего уровня)Expr => Expr
от его расширения - вы не можете написать "просто" inline def something[T: Type](a: Expr[T])(using Quotes): Expr[T] = ...
- тело макроса - это (нестрочный) def
, принимающий Type
и Expr
и Quotes
и возвращающий один Expr
. inline def
должен заключить в кавычки одиночный Expr
с помощью ${}
... или просто быть обычным определением, которое можно скопировать на сайт вызова и разрешить там. Но тогда он не выводит из кавычек никаких Expr
sИтак, это либо:
// Quotes API requires separate inline def sth = ${ sthImpl }
// and def sthImpl[T: Type](a: Expr[...])(using Quotes): Expr[...]
inline def parseJson[T](x: String)(using r: Reader[T]): Either[Throwable, T] =
${ parseJsonImpl[T]('x)('r) }
// Since it builds a complete TASTy, it cannot "defer" implicit
// resolution to callsite. It either already gets value to put
// into using, or has to import quotes.*, quotes.reflect.* and
// then use Expr.summon[T].getOrElse(...)
def parseJsonImpl[T:Type](
x: Expr[String]
)(
reader: Expr[Reader[T]]
)(using Quotes): Expr[Either[String, T]] = '{
try
Right(read[T]($x)(using $reader))
catch
case e: Throwable => Left(s"Deserialization error: ${e.getMessage}")
}
или
// inline def NOT using quotation API - NO Quotes, NO '{}
inline def parseJson[T: Reader](x: String): Either[String, T] = {
try
// Here, you can use something like a "vampyre macros" and
// let the compiler expand macros at the call site
Right(read[T](x))
catch
case e: Throwable => Left(s"Deserialization error: ${e.getMessage}")
}
Вы правы, я исправил ответ.
Большое спасибо! Дополнительно (при наличии r: Reader[T]) необходимо во втором примере.