使用 flatMap 进行类型解析
Type resolution with flatMap
为什么我需要在第一行添加类型注释?
c.get[List[String]]("primary-group")
在 flatMap
之后是 Decoder.Result[List[String]]
它应该保持顶部类型并且是 Decoder.Result[String]
但它变为 Either[DecodingFailure, String]。为什么?问题是 dependent type
?
case class JWTPayload(primaryGroup: Group, groupMember: List[Group], name: String, pid: String)
implicit val jwtPayloadDecoder: Decoder[JWTPayload] = Decoder.instance(c =>
(
c.get[List[String]]("primary-group").flatMap(l => if(l.size == 1) l.head.asRight else DecodingFailure("", c.history).asLeft) : Decoder.Result[String],
c.get[List[String]]("group-member"),
c.get[String]("name"),
c.get[String]("pid")
).map4(
JWTPayload
)
)
没有: Decoder.Result[String
我得到
Error:(43, 7) value map4 is not a member of (scala.util.Either[io.circe.DecodingFailure,String], io.circe.Decoder.Result[List[String]], io.circe.Decoder.Result[String], io.circe.Decoder.Result[String])
possible cause: maybe a semicolon is missing before `value map4'?
).map4(
谢谢
这不是一个完整的答案,但我希望它能提供一些见解。这里的关键部分是 map4
是如何实现的。从 cats 0.9 开始,它是通过 cats.syntax.TupleCartesianSyntax
特性及其隐式 catsSyntaxTuple4Cartesian
完成的,它将一个 4 元组包装成 cats.syntax.Tuple4CartesianOps
class(在 cats 1.0 "cartesian" 中更改为 "semigroupal")。 Boilerplate.scala 为最多 22 个元组自动生成此代码。自动生成的代码看起来像这样:
implicit def catsSyntaxTuple4Cartesian[F[_], A0, A1, A2, A3](t4: Tuple4[F[A0], F[A1], F[A2], F[A3]]): Tuple4CartesianOps[F, A0, A1, A2, A3] = new Tuple4CartesianOps(t4)
private[syntax] final class Tuple4CartesianOps[F[_], A0, A1, A2, A3](t4: Tuple4[F[A0], F[A1], F[A2], F[A3]]) {
def map4[Z](f: (A0, A1, A2, A3) => Z)(implicit functor: Functor[F], cartesian: Cartesian[F]): F[Z] = Cartesian.map4(t4._1, t4._2, t4._3, t4._4)(f)
def contramap4[Z](f: Z => (A0, A1, A2, A3))(implicit contravariant: Contravariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.contramap4(t4._1, t4._2, t4._3, t4._4)(f)
def imap4[Z](f: (A0, A1, A2, A3) => Z)(g: Z => (A0, A1, A2, A3))(implicit invariant: Invariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.imap4(t4._1, t4._2, t4._3, t4._4)(f)(g)
def apWith[Z](f: F[(A0, A1, A2, A3) => Z])(implicit apply: Apply[F]): F[Z] = apply.ap4(f)(t4._1, t4._2, t4._3, t4._4)
}
注意 F[_]
(仿函数)类型参数。实际上,此代码将 map4
方法添加到任何 4 元组,其中每个内部类型在某些类型上都是相同的函子。
所以假设你做了 import cats.implicits._
,在(部分)隐式解析之后你的代码实际上是这样的:
cats.implicits.catsSyntaxTuple4Cartesian[Decoder.Result, String, List[String], String, String](
c.get[List[String]]("primary-group").flatMap(l => if (l.size == 1) l.head.asRight else DecodingFailure("", c.history).asLeft): Decoder.Result[String],
c.get[List[String]]("group-member"),
c.get[String]("name"),
c.get[String]("pid")
).map4[JWTPayload](
JWTPayload
)
当您不指定 Decoder.Result[String]
时,Scala 编译器不够智能,无法将 Either[DecodingFailure, String]
拆分为函子类型 Either[DecodingFailure, _]
和 String
以及然后
会有匹配的Functor
和Cartesian
隐式对象(实际上是由cats.implicits
对象通过cats.instances.AllInstances
和cats.instances.EitherInstances
提供的特质)
它将匹配用于元组中其他 3 个字段的仿函数类型(即 Decoder.Result[_]
)。
所以我认为这种行为是 map4
通过隐式 Ops-class 添加到 4 元组这一事实和基础类型是 Either
这是 2 位泛型类型而不是简单的 Functor
.
为什么我需要在第一行添加类型注释?
c.get[List[String]]("primary-group")
在 flatMap
之后是 Decoder.Result[List[String]]
它应该保持顶部类型并且是 Decoder.Result[String]
但它变为 Either[DecodingFailure, String]。为什么?问题是 dependent type
?
case class JWTPayload(primaryGroup: Group, groupMember: List[Group], name: String, pid: String)
implicit val jwtPayloadDecoder: Decoder[JWTPayload] = Decoder.instance(c =>
(
c.get[List[String]]("primary-group").flatMap(l => if(l.size == 1) l.head.asRight else DecodingFailure("", c.history).asLeft) : Decoder.Result[String],
c.get[List[String]]("group-member"),
c.get[String]("name"),
c.get[String]("pid")
).map4(
JWTPayload
)
)
没有: Decoder.Result[String
我得到
Error:(43, 7) value map4 is not a member of (scala.util.Either[io.circe.DecodingFailure,String], io.circe.Decoder.Result[List[String]], io.circe.Decoder.Result[String], io.circe.Decoder.Result[String])
possible cause: maybe a semicolon is missing before `value map4'?
).map4(
谢谢
这不是一个完整的答案,但我希望它能提供一些见解。这里的关键部分是 map4
是如何实现的。从 cats 0.9 开始,它是通过 cats.syntax.TupleCartesianSyntax
特性及其隐式 catsSyntaxTuple4Cartesian
完成的,它将一个 4 元组包装成 cats.syntax.Tuple4CartesianOps
class(在 cats 1.0 "cartesian" 中更改为 "semigroupal")。 Boilerplate.scala 为最多 22 个元组自动生成此代码。自动生成的代码看起来像这样:
implicit def catsSyntaxTuple4Cartesian[F[_], A0, A1, A2, A3](t4: Tuple4[F[A0], F[A1], F[A2], F[A3]]): Tuple4CartesianOps[F, A0, A1, A2, A3] = new Tuple4CartesianOps(t4)
private[syntax] final class Tuple4CartesianOps[F[_], A0, A1, A2, A3](t4: Tuple4[F[A0], F[A1], F[A2], F[A3]]) {
def map4[Z](f: (A0, A1, A2, A3) => Z)(implicit functor: Functor[F], cartesian: Cartesian[F]): F[Z] = Cartesian.map4(t4._1, t4._2, t4._3, t4._4)(f)
def contramap4[Z](f: Z => (A0, A1, A2, A3))(implicit contravariant: Contravariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.contramap4(t4._1, t4._2, t4._3, t4._4)(f)
def imap4[Z](f: (A0, A1, A2, A3) => Z)(g: Z => (A0, A1, A2, A3))(implicit invariant: Invariant[F], cartesian: Cartesian[F]): F[Z] = Cartesian.imap4(t4._1, t4._2, t4._3, t4._4)(f)(g)
def apWith[Z](f: F[(A0, A1, A2, A3) => Z])(implicit apply: Apply[F]): F[Z] = apply.ap4(f)(t4._1, t4._2, t4._3, t4._4)
}
注意 F[_]
(仿函数)类型参数。实际上,此代码将 map4
方法添加到任何 4 元组,其中每个内部类型在某些类型上都是相同的函子。
所以假设你做了 import cats.implicits._
,在(部分)隐式解析之后你的代码实际上是这样的:
cats.implicits.catsSyntaxTuple4Cartesian[Decoder.Result, String, List[String], String, String](
c.get[List[String]]("primary-group").flatMap(l => if (l.size == 1) l.head.asRight else DecodingFailure("", c.history).asLeft): Decoder.Result[String],
c.get[List[String]]("group-member"),
c.get[String]("name"),
c.get[String]("pid")
).map4[JWTPayload](
JWTPayload
)
当您不指定 Decoder.Result[String]
时,Scala 编译器不够智能,无法将 Either[DecodingFailure, String]
拆分为函子类型 Either[DecodingFailure, _]
和 String
以及然后
会有匹配的
Functor
和Cartesian
隐式对象(实际上是由cats.implicits
对象通过cats.instances.AllInstances
和cats.instances.EitherInstances
提供的特质)它将匹配用于元组中其他 3 个字段的仿函数类型(即
Decoder.Result[_]
)。
所以我认为这种行为是 map4
通过隐式 Ops-class 添加到 4 元组这一事实和基础类型是 Either
这是 2 位泛型类型而不是简单的 Functor
.