Scala 在播放json读/写宏中键入参数

Scala 在播放json读/写宏中键入参数,scala,scala-macros,play-json,Scala,Scala Macros,Play Json,我有一个参数化的case类case类[T](名称:String,T:T),我希望使用play-json(2.5)对其进行序列化/反序列化 当然,如果我没有类型T的等价项,我就不能拥有它,所以我定义 object CaseClass { implicit def reads[T: Reads] = Json.reads[CaseClass[T]] } 但我得到以下编译器错误: overloaded method value apply with alternatives: [B](f:

我有一个参数化的case类
case类[T](名称:String,T:T)
,我希望使用play-json(2.5)对其进行序列化/反序列化

当然,如果我没有类型
T
的等价项,我就不能拥有它,所以我定义

object CaseClass {
  implicit def reads[T: Reads] = Json.reads[CaseClass[T]]
}
但我得到以下编译器错误:

overloaded method value apply with alternatives:
   [B](f: B => (String, T))(implicit fu: play.api.libs.functional.ContravariantFunctor[play.api.libs.json.Reads])play.api.libs.json.Reads[B] <and>
   [B](f: (String, T) => B)(implicit fu: play.api.libs.functional.Functor[play.api.libs.json.Reads])play.api.libs.json.Reads[B]
   cannot be applied to ((String, Nothing) => CaseClass[Nothing])
最令人惊讶的是,当我使用
Json.format
宏时,两个错误都没有发生


我知道我对旁路问题有不同的解决方案(使用
Json.format
,手工编写(反)序列化程序,…),但我很好奇为什么会出现这种情况。

这可能是
Json.reads
宏的限制,也可能是类型推断,或者两者兼而有之。类型推断至少与此有一点关系,因为您可以在错误消息中看到有东西被推断为
Nothing

如果使用编译器标志
-Ymacro debug lite
,则可以看到生成的AST宏

implicit def reads[T](implicit r: Reads[T]): Reads[CaseClass[T]] = 
  Json.reads[CaseClass[T]]
翻译为:

_root_.play.api.libs.json.JsPath.$bslash("name").read(json.this.Reads.StringReads)
  .and(_root_.play.api.libs.json.JsPath.$bslash("t").read(r))
  .apply((CaseClass.apply: (() => <empty>)))
不幸的是,它没有编译,因为没有提供
CaseClass.apply
的类型参数,并且推断为
Nothing
。手动将
T
添加到
apply
可以解决问题,但宏可能不知道
CaseClass[T]
中的
T
很重要

为了更详细地解决类型推断问题,使用
读取
组合符,我们调用
FunctionalBuilder.CanBuild2#apply
,它需要a
(A1,A2)=>B
。但是编译器无法正确地推断出
A2

对于
写入
,有一个类似的问题,我们需要a
B=>(A1,A2)
,但编译器无法正确推断
B
A2
(分别是
CaseClass[T]
T


Format
需要上述两个函数,并且编译器能够推理出
A2
必须是
T

我仍然不明白的是,为什么编译器错误会说
FunctionalBuilder.CanBuild2#apply
在两种方法之间存在歧义,其中一种方法显然无法应用(
Reads
不是逆变函子,参数的类型与
B=>(String,Nothing)
),虽然它能够为
写入操作选择正确的类型,但您已经清楚地解释了类型推断的问题。从您所说的,我猜
格式
的情况可以解释为,在不变函子上类型推断更容易,因为有更多的参数(如您所说的两个函数)。
_root_.play.api.libs.json.JsPath.$bslash("name").read(json.this.Reads.StringReads)
  .and(_root_.play.api.libs.json.JsPath.$bslash("t").read(r))
  .apply((CaseClass.apply: (() => <empty>)))
implicit def reads[T](implicit w: Reads[T]): Reads[CaseClass[T]] = (
  (JsPath \ "name").read(Reads.StringReads) and
  (JsPath \ "t" ).read(r)
)(CaseClass.apply _)