播放JSON InvariantFunctor

Kev*_*ith 4 scala functor playframework

Play的JSON 包括:FunctorInvariant Functor:

我以前见过Functor:

trait Functor[M[_]] extends Variant[M] {

  def fmap[A, B](m: M[A], f: A => B): M[B]

}
Run Code Online (Sandbox Code Playgroud)

但是,从概念上讲,它为什么需要提供这两种功能f1,并f2InvariantFunctor

trait InvariantFunctor[M[_]] extends Variant[M] {

  def inmap[A, B](m: M[A], f1: A => B, f2: B => A): M[B]

}
Run Code Online (Sandbox Code Playgroud)

Tra*_*own 11

这个答案中,我快速解释了为什么Writes不是一个仿函数 - 也就是说,为什么如果我们有Writes[A]一个函数,A => B我们就无法Writes[B]以同样的方式创建一个函数Reads.

正如我在该答案中所指出的,Writes它不是一个普通的(协变)仿函数,但它是一个逆变函子,这意味着如果我们有Writes[A]一个函数B => A,我们就可以创建一个函数Writes[B].

Format涵括两者的功能ReadsWrites,这意味着它既不是仿函数也不是逆变函子,但它是一个不变的算符(和它的真正唯一的类型与你在游戏的上下文中运行到一个不变的仿函数实例).

要知道为什么会这样,假设我们有以下两种类型:

case class Foo(i: Int, s: String)
case class Bar(s: String, i: Int)
Run Code Online (Sandbox Code Playgroud)

假设我们有一个Format实例Foo:

import play.api.libs.json._
import play.api.libs.functional.syntax._

implicit val fooFormat = Json.format[Foo]
Run Code Online (Sandbox Code Playgroud)

但无论出于何种原因,我们都无法以相同的方式创建一个 - Bar我们希望从中获取它Foo.这是不够的,我们知道如何创建一个BarFoo,反之亦然,但如果我们可以是双向的,我们可以用不变的仿函数Format:

implicit val barFormat = fooFormat.inmap[Bar](
  foo => Bar(foo.s, foo.i),
  bar => Foo(bar.i, bar.s)
)
Run Code Online (Sandbox Code Playgroud)

这是因为我们可以把它想象Format成一个双向管道,它允许我们输入一个JsValue并输出一些A,或者放入A并输出一个JsValue.如果我们想将双向管道Format[A]转换成双向管道Format[B],我们需要两侧的适配器(即两者A => BB => A).