带有标记类型和播放 Json 格式类型类派生的编译错误
Posted
技术标签:
【中文标题】带有标记类型和播放 Json 格式类型类派生的编译错误【英文标题】:Compile error with Tagged Type and Play Json Format typeclass derivation 【发布时间】:2021-09-24 19:20:16 【问题描述】:在下面的示例中,我想为我的类的 id 使用标记类型。我创建了一个实用程序特征来减少一些样板(标签/读取/写入声明):
import java.util.UUID
import play.api.libs.json.Format, Json, Reads, Writes
trait Opaque[A]
protected type Tagged[U] = type Tag = U
type @@[U, T] = U with Tagged[T]
trait Tag
def tag(a: A): A @@ Tag = a.asInstanceOf[A @@ Tag]
def untag(a: A @@ Tag): A = a
implicit def reads(implicit r: Reads[A]): Reads[A @@ Tag] =
r.map(tag)
implicit def writes(implicit w: Writes[A]): Writes[A @@ Tag] =
w.contramap(untag)
implicit def format(implicit r: Reads[A], w: Writes[A]): Format[A @@ Tag] =
Format(reads(r), writes(w))
final case class Foo(id: Foo.FooId.T, f1: Boolean)
object Foo
object FooId extends Opaque[UUID]
type T = UUID @@ Tag
import FooId._
implicit val fmt: Format[Foo] = Json.format[Foo]
final case class Bar(id: Bar.BarId.T, fooId: Foo.FooId.T, b1: String)
object Bar
object BarId extends Opaque[UUID]
type T = UUID @@ Tag
import Foo.FooId._
import BarId._
implicit val format: Format[Bar] = Json.format[Bar]
我从编译器收到以下错误:
implicit val format: Format[Bar] = Json.format[Bar]
^
<pastie>:43: error: No instance of play.api.libs.json.Format is available for Opaque.<refinement>, Opaque.<refinement> in the implicit scope (Hint: if declared in the same file, make sure it's declared before)
我无法解释为什么我会出现这种行为,错误消息并不明确。我正在为FooId
和BarId
导入Format
,以导出Bar
类的格式。
【问题讨论】:
【参考方案1】:事情是隐含的名字很重要。
一个非常简单的例子如下:
object MyObject
implicit val i: Int = ???
import MyObject._
implicit val i: String = ???
// implicitly[Int] // doesn't compile
// implicitly[String] // doesn't compile
但是
object MyObject
implicit val i: Int = ???
import MyObject._
implicit val i1: String = ???
implicitly[Int] // compiles
implicitly[String] // compiles
如果您希望派生Json.format[Bar]
起作用,则应在范围内隐含Format[Bar.BarId.T]
、Format[Foo.FooId.T]
,即Format
字段的Bar
实例。如果您进行唯一的导入
import Foo.FooId._
implicitly[Format[Foo.FooId.T]] // compiles
和
import BarId._
implicitly[Format[Bar.BarId.T]] // compiles
但如果你同时导入两者,因为隐含的名称会发生冲突
import Foo.FooId._
import BarId._
// implicitly[Format[Foo.FooId.T]] // doesn't compiles
// implicitly[Format[Bar.BarId.T]] // doesn't compiles
例如,您可以将trait Tag
移出trait Opaque
并进行唯一的导入。那么
implicitly[Format[Foo.FooId.T]]
implicitly[Format[Bar.BarId.T]]
Json.format[Bar]
将编译。
https://youtu.be/1h8xNBykZqM?t=681 我们在设计隐式时犯的一些错误,错误 #1
NullPointerException on implicit resolution
【讨论】:
谢谢你的回答,我明白问题出在哪里了。我会接受的。我在理解这部分时遇到了问题:“例如,您可以将 trait Tag 移到 trait Opaque 之外并进行唯一的导入。”。请问你能详细说明吗?可以肯定的是,我正在寻找的是为每个 Id 使用不同的标签,通过将Tag
移到外面,我将不得不复制特征。
@stankoua 哦,我明白你为什么想要Tag
inner。以上是关于带有标记类型和播放 Json 格式类型类派生的编译错误的主要内容,如果未能解决你的问题,请参考以下文章