问题描述
我有一个经典的 Encoder
类型类.
I have a classic Encoder
typeclass.
trait Encoder[A] {
def encode(a: A): String
}
我有两个问题
问题1:差异从何而来:
Question 1 : where does the divergence comes from :
[error] … diverging implicit expansion for type sbopt.Test.Encoder[None.type]
[error] starting with value stringEncoder in object Test
[error] show(None)
implicit val stringEncoder = new Encoder[String] {
override def encode(a: String): String = a
}
implicit def optionEncoder[A: Encoder]: Encoder[Option[A]] =
(a: Option[A]) => {
val encoderA = implicitly[Encoder[A]]
a.fold("")(encoderA.encode)
}
implicit def someEncoder[A: Encoder]: Encoder[Some[A]] =
(a: Some[A]) => {
val encoderA = implicitly[Encoder[A]]
encoderA.encode(a.get)
}
implicit def noneEncoder[A: Encoder]: Encoder[None.type] =
(_: None.type) => ""
def show[A: Encoder](a: A) = println(implicitly[Encoder[A]].encode(a))
show(None)
问题2:我大概看到了编码器不是协变的.优点和缺点是什么?
Question 2 : I saw in circe that the Encoder is not contravariant. What are the pros and cons ?
trait Encoder[-A] {
def encode(a: A): String
}
implicit val stringEncoder: Encoder[String] = (a: String) => a
implicit def optionEncoder[A: Encoder]: Encoder[Option[A]] =
(a: Option[A]) => {
val encoderA = implicitly[Encoder[A]]
a.fold("")(encoderA.encode)
}
def show[A: Encoder](a: A) = println(implicitly[Encoder[A]].encode(a))
show(Option("value"))
show(Some("value"))
show(None)
推荐答案
关于1.
您对 noneEncoder
的定义不好.您还有一个额外的上下文绑定(甚至还有额外的类型参数).
Your definition of noneEncoder
is not good. You have an extra context bound (and even extra type parameter).
使用
implicit def noneEncoder/*[A: Encoder]*/: Encoder[None.type] =
(_: None.type) => ""
它编译:
show[Option[String]](None)
show[None.type](None)
show(None)
您最初对 noneEncoder
的定义意味着您为 None.type
拥有一个 Encoder
实例,前提是您有一些实例.> A
(不受限制,即可以推断).通常,如果您具有唯一的隐式(或至少只有唯一的更高优先级的隐式),则此方法有效.例如,如果只有 stringEncoder
和原始的 noneEncoder
,则 show [None.type](None)
和 show(None)
即可编译.
Your original definition of noneEncoder
meant that you had an instance of Encoder
for None.type
provided you had an instance for some A
(not constrained i.e. to be inferred). Normally this works if you have the only implicit (or at least the only higher-priority implicit). For example if there were only stringEncoder
and original noneEncoder
then show[None.type](None)
and show(None)
would compile.
关于2.
PROS .使用反变的 Encoder
trait Encoder[-A] {
def encode(a: A): String
}
您可以删除 someEncoder
和 noneEncoder
, optionEncoder
就足够了
you can remove someEncoder
and noneEncoder
, optionEncoder
will be enough
show(Some("a"))
show[Option[String]](Some("a"))
show[Option[String]](None)
show[None.type](None)
show(None)
CONS..有人认为协变类型类的行为与直觉相反:
CONS. Some people believe that contravariant type classes behave counterintuitively:
https://github.com/scala/bug/issues/2509
https://groups.google.com/g/scala-language/c/ZE83TvSWpT4/m/YiwJJLZRmlcJ
也可能相关:在Scala 2.13中,如何隐式使用[值单例类型]?
这篇关于如何在Scala中使用编码器类型类处理Option的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!