问题描述
我正在使用Circe,发现我不太舒服,想了解引擎盖下发生了什么事?
从根本上讲,这并不是一个真正的问题。我也只是玩转来测试一些东西。因此本来可以直接JsonObject
中进行解码,但这已经不重要了。
val jobjectStr = """{
| "idProperty": 1991264,| "nIndex": 0,| "sPropertyValue": "0165-5728"
| }""".stripMargin
val jobject = decode[Json](jobjectStr).flatMap{ json =>
json.as[JsonObject]
}
我的问题是,Everer的flaveMap签名,逆变以及这里发生的事情:
我们有以下几种类型:
decode[Json](jobjectStr): Either[Error,Json]
json.as[JsonObject]: Decoder.Result[JsonObject]
circe定义的地方
final type Result[A] = Either[DecodingFailure,A]
和
sealed abstract class DecodingFailure(val message: String) extends Error {
现在flatMap的签名是:
def flatMap[A1 >: A,B1](f: B => Either[A1,B1]): Either[A1,B1]
换句话说,只谈论类型就像我的代码一样
Either[Error,Json] flatMap Either[DecodingFailure,JsonObject]
因此,我的问题是: DecodingFailure >: Error
不正确
确实,完整表达式的类型是:
decode[Json](jobjectStr).flatMap{ json =>
json.as[JsonObject]
}: Either[Error,JsonObject]
因此,我感到困惑,因为我的理解是,flatMap签名中Either的第一个参数的类型是相反的。这里似乎有一些奇怪的最小上限推断正在发生……但是我不确定为什么或是否确实如此。
任何解释吗?
解决方法
这实际上不是差异问题。 A1 >: A
只是告诉我们,如果编译器必须寻找最小的上限值,则结果类型A1
可能必须是接收到的类型A
的超类型。绑定(LUB)。 (我认为A1
描述中使用f: B => ...
有点令人困惑。)
请考虑以下内容:
class Base
class SubA extends Base
class SubB extends Base
Either.cond(true,"a string",new SubA)
.flatMap(Either.cond(true,_,new SubB))
//res0: scala.util.Either[Base,String] = Right(a string)
请注意结果如何Either[Base,String]
,因为Base
是SubA
和SubB
的LUB。
因此,首先,我们需要了解编译器将始终尝试推断允许编译的类型。避免编译的唯一真实方法是使用隐式。
(不确定这是语言规范的一部分,还是编译器实现的详细信息,还是所有编译器都共有的东西,还是错误或功能)。
现在,让我们从一个简单的示例列表和::
开始。
sealed trait List[+A] {
def ::[B >: A](b: B): List[B] = Cons(b,this)
}
final case class Cons[+A](head: A,tail: List[A]) extends List[A]
final case object Nil extends List[Nothing]
因此,假设编译器将始终允许诸如x :: list
之类的某些代码将始终进行编译。然后,我们有三种情况:
-
x
是 A 类型,而list
是 List [A] ,因此很明显,返回值必须是类型为列表[A] 。 -
x
是 C 类型,而list
是列表[A] ,而 C 是 A (C <: A
)的子类型。然后,编译器简单地将x
转换为 A 类型,然后该过程继续与上一个相同。 -
x
是 D 类型,而list
是列表[A] ,而 D 是不是 A 的子类型。然后,编译器找到一个新类型 B ,它是 D 和 A 之间的LUB,编译器最后将x
都转换为的类型为 B ,而list
则为列表[B] (这可能是由于协方差)一个。
另外,请注意,由于存在 Any 和 Nothing 之类的类型,因此两种类型之间总是存在一个LUB。
现在让我们看看 和flatMap
。
sealed trait Either[+L,+R] {
def flatMap[LL >: L,RR](f: R => Either[LL,RR]): Either[LL,RR]
}
final case class Left[+L](l: L) extends Either[L,Nothing]
final case clas Right[+R](r: R) extends Either[Nothing,R]
现在,假设我的左侧是错误,我觉得这种在两个可能的左侧之间返回LUB的行为是最好的,因为最后我会遇到第一个错误,或者第二个错误或最终值,因此,由于我不知道这两个错误中的哪一个,因此该错误必须是某种类型,它封装了两种可能的错误。