组织与类型关联的 Scala 隐式
Organizing Scala implicits associated with a type
我想介绍一些类型来表示更大类型的字段的可能值。此字段需要 encode/decode to/from JSON 并且能够 written/read 到数据库。
我对 Scala 还是个新手,我想要的类型是求和类型 Status = NotVerified | Correct | Wrong
。因为我想有一个与每个构造函数关联的字符串表示,所以我创建了一个带有 String
参数的密封案例 class,然后是扩展该案例 class 的对象。为了能够 encode/decode,我还需要有隐式,但我不确定如何构造它。我可以将它们放在对象内的新对象中,如下所示:
sealed case class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
object implicits {
implicit val encodeStatusJson: Encoder[Status] =
_.name.asJson
implicit val decodeStatusJson: Decoder[Status] =
Decoder.decodeString.map(Status(_))
implicit val encodeStatus: MappedEncoding[Status, String] =
MappedEncoding[Status, String](_.name)
implicit val decodeStatus: MappedEncoding[String, Status] =
MappedEncoding[String, Status](Status(_))
}
}
…然后在需要的地方明确地import
这些,但这非常…明确。
组织此类类型 + 隐式集合的好方法是什么?
如果您添加 apply
方法,您可以从 String
创建适当的 Status
,这应该使 Decoder
正常工作。并使 Status
抽象
sealed abstract class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
def apply(name: String): Status = name match {
case "not_verified" => NotVerified
case "correct" => Correct
case _ => Wrong
}
}
我认为您现有的隐含函数仍然有效,但我不知道那些特定的库...
通常的做法是定义一个sealed trait
:
sealed trait Status {
def name: String
}
object Status {
case object NotVerified extends Status {
val name = "not_verified"
}
case object Correct extends Status {
val name = "correct"
}
case object Wrong extends Status {
val name = "wrong"
}
}
或 sealed abstract class
,在当前的 Scala 版本中可能看起来更好:
sealed abstract class Status(val name: String)
object Status {
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
为了避免导入隐含的需要,它们可以直接放在类型的伴生对象中。另请参阅问题 Where does Scala look for implicits? 以获取更多详细信息,尤其是 类型的伴随对象 部分。
是的,为这样的枚举定义隐式很容易变得重复。您必须求助于反射或宏。我推荐使用 Enumeratum 库,它还集成了 Circe 和 Quill。
这是 Circe 的示例:
import enumeratum.values._
sealed abstract class Status(val value: String) extends StringEnumEntry {
def name: String = value
}
object Status extends StringEnum[Status] with StringCirceEnum[Status] {
val values = findValues
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
并且您可以在不明确定义任何 encoders/decoders 或从 Status
:
导入任何内容的情况下使用它
scala> import io.circe.syntax._
scala> val status: Status = Status.Correct
status: Status = Correct
scala> status.asJson
res1: io.circe.Json = "correct"
scala> Decoder[Status].decodeJson(Json.fromString("correct"))
res2: io.circe.Decoder.Result[Status] = Right(Correct)
我想介绍一些类型来表示更大类型的字段的可能值。此字段需要 encode/decode to/from JSON 并且能够 written/read 到数据库。
我对 Scala 还是个新手,我想要的类型是求和类型 Status = NotVerified | Correct | Wrong
。因为我想有一个与每个构造函数关联的字符串表示,所以我创建了一个带有 String
参数的密封案例 class,然后是扩展该案例 class 的对象。为了能够 encode/decode,我还需要有隐式,但我不确定如何构造它。我可以将它们放在对象内的新对象中,如下所示:
sealed case class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
object implicits {
implicit val encodeStatusJson: Encoder[Status] =
_.name.asJson
implicit val decodeStatusJson: Decoder[Status] =
Decoder.decodeString.map(Status(_))
implicit val encodeStatus: MappedEncoding[Status, String] =
MappedEncoding[Status, String](_.name)
implicit val decodeStatus: MappedEncoding[String, Status] =
MappedEncoding[String, Status](Status(_))
}
}
…然后在需要的地方明确地import
这些,但这非常…明确。
组织此类类型 + 隐式集合的好方法是什么?
如果您添加 apply
方法,您可以从 String
创建适当的 Status
,这应该使 Decoder
正常工作。并使 Status
抽象
sealed abstract class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
def apply(name: String): Status = name match {
case "not_verified" => NotVerified
case "correct" => Correct
case _ => Wrong
}
}
我认为您现有的隐含函数仍然有效,但我不知道那些特定的库...
通常的做法是定义一个sealed trait
:
sealed trait Status {
def name: String
}
object Status {
case object NotVerified extends Status {
val name = "not_verified"
}
case object Correct extends Status {
val name = "correct"
}
case object Wrong extends Status {
val name = "wrong"
}
}
或 sealed abstract class
,在当前的 Scala 版本中可能看起来更好:
sealed abstract class Status(val name: String)
object Status {
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
为了避免导入隐含的需要,它们可以直接放在类型的伴生对象中。另请参阅问题 Where does Scala look for implicits? 以获取更多详细信息,尤其是 类型的伴随对象 部分。
是的,为这样的枚举定义隐式很容易变得重复。您必须求助于反射或宏。我推荐使用 Enumeratum 库,它还集成了 Circe 和 Quill。 这是 Circe 的示例:
import enumeratum.values._
sealed abstract class Status(val value: String) extends StringEnumEntry {
def name: String = value
}
object Status extends StringEnum[Status] with StringCirceEnum[Status] {
val values = findValues
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
并且您可以在不明确定义任何 encoders/decoders 或从 Status
:
scala> import io.circe.syntax._
scala> val status: Status = Status.Correct
status: Status = Correct
scala> status.asJson
res1: io.circe.Json = "correct"
scala> Decoder[Status].decodeJson(Json.fromString("correct"))
res2: io.circe.Decoder.Result[Status] = Right(Correct)