play-jsonread/write宏中的类型参数
Type parameter in play-json read/write macro
我有一个参数化的案例 class CaseClass[T](name: String, t: T)
我想 serialization/deserialization 使用 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.writes
宏执行相同的操作,我会收到错误消息
type mismatch;
found : CaseClass[Nothing] => (String, Nothing)
required: CaseClass[T] => (String, T)
最令人惊讶的是,当我使用 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>)))
清理后,看起来像:
implicit def reads[T](implicit w: Reads[T]): Reads[CaseClass[T]] = (
(JsPath \ "name").read(Reads.StringReads) and
(JsPath \ "t" ).read(r)
)(CaseClass.apply _)
不幸的是,它没有编译,因为 CaseClass.apply
的类型参数没有提供并且被推断为 Nothing
。手动将 T
添加到 apply
可以解决问题,但宏可能不知道 CaseClass[T]
中的 T
很重要。
为了更详细地解决类型推断问题,我们使用 Reads
组合器调用 FunctionalBuilder.CanBuild2#apply
,它需要一个 (A1, A2) => B
。但是编译器无法正确推断 A2
.
对于 Writes
,存在类似的问题,我们需要一个 B => (A1, A2)
,但编译器无法正确推断 B
或 A2
(这是 CaseClass[T]
和 T
。
Format
需要上述两个函数,编译器能够推断出 A2
必须是 T
.
我有一个参数化的案例 class CaseClass[T](name: String, t: T)
我想 serialization/deserialization 使用 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.writes
宏执行相同的操作,我会收到错误消息
type mismatch;
found : CaseClass[Nothing] => (String, Nothing)
required: CaseClass[T] => (String, T)
最令人惊讶的是,当我使用 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>)))
清理后,看起来像:
implicit def reads[T](implicit w: Reads[T]): Reads[CaseClass[T]] = (
(JsPath \ "name").read(Reads.StringReads) and
(JsPath \ "t" ).read(r)
)(CaseClass.apply _)
不幸的是,它没有编译,因为 CaseClass.apply
的类型参数没有提供并且被推断为 Nothing
。手动将 T
添加到 apply
可以解决问题,但宏可能不知道 CaseClass[T]
中的 T
很重要。
为了更详细地解决类型推断问题,我们使用 Reads
组合器调用 FunctionalBuilder.CanBuild2#apply
,它需要一个 (A1, A2) => B
。但是编译器无法正确推断 A2
.
对于 Writes
,存在类似的问题,我们需要一个 B => (A1, A2)
,但编译器无法正确推断 B
或 A2
(这是 CaseClass[T]
和 T
。
Format
需要上述两个函数,编译器能够推断出 A2
必须是 T
.