Tagged Type 和 Play 编译错误 Json Format typeclass derivation
Compile error with Tagged Type and Play Json Format typeclass derivation
在下面的示例中,我想为我的 classes 的 ID 使用标记类型。我创建了一个实用特性来减少一些样板文件(tags/reads/writes 声明):
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
class.
的格式
问题是隐含的名称很重要。
下面是一个非常简单的例子:
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]
,即 Bar
字段的 Format
个实例。如果你只做 import
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
在下面的示例中,我想为我的 classes 的 ID 使用标记类型。我创建了一个实用特性来减少一些样板文件(tags/reads/writes 声明):
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
class.
问题是隐含的名称很重要。
下面是一个非常简单的例子:
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]
,即 Bar
字段的 Format
个实例。如果你只做 import
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