Skip to content

Commit

Permalink
Experimenting with optics
Browse files Browse the repository at this point in the history
  • Loading branch information
travisbrown committed Nov 2, 2015
1 parent 0a13800 commit 2ee2da9
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 23 deletions.
16 changes: 9 additions & 7 deletions optics/src/main/scala/io/circe/optics/JsonNumberOptics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,30 @@ import monocle.{ Iso, Prism }
* @author Travis Brown
*/
trait JsonNumberOptics {
val jsonNumberToDouble: Prism[JsonNumber, Double] = Prism[JsonNumber, Double]{ n =>
lazy val jsonNumberToDouble: Prism[JsonNumber, Double] = Prism[JsonNumber, Double]{ n =>
val d = n.toDouble
if (JsonNumber.eqJsonNumber.eqv(JsonDouble(d), n)) Some(d) else None
}(JsonDouble(_))

val jsonNumberToBigInt: Prism[JsonNumber, BigInt] =
lazy val jsonNumberToBigInt: Prism[JsonNumber, BigInt] =
Prism[JsonNumber, BigInt](_.toBigInt)(b =>
JsonBigDecimal(BigDecimal(b, MathContext.UNLIMITED))
)

val jsonNumberToLong: Prism[JsonNumber, Long] =
lazy val jsonNumberToLong: Prism[JsonNumber, Long] =
Prism[JsonNumber, Long](_.toLong)(JsonLong(_))

val jsonNumberToInt: Prism[JsonNumber, Int] =
lazy val jsonNumberToInt: Prism[JsonNumber, Int] =
Prism[JsonNumber, Int](_.toInt)(i => JsonLong(i.toLong))

val jsonNumberToShort: Prism[JsonNumber, Short] =
lazy val jsonNumberToShort: Prism[JsonNumber, Short] =
Prism[JsonNumber, Short](_.toShort)(s => JsonLong(s.toLong))

val jsonNumberToByte: Prism[JsonNumber, Byte] =
lazy val jsonNumberToByte: Prism[JsonNumber, Byte] =
Prism[JsonNumber, Byte](_.toByte)(b => JsonLong(b.toLong))

val jsonNumberToBigDecimal: Iso[JsonNumber, BigDecimal] =
lazy val jsonNumberToBigDecimal: Iso[JsonNumber, BigDecimal] =
Iso[JsonNumber, BigDecimal](_.toBigDecimal)(JsonBigDecimal(_))
}

object JsonNumberOptics extends JsonNumberOptics
10 changes: 6 additions & 4 deletions optics/src/main/scala/io/circe/optics/JsonObjectOptics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,22 @@ import scalaz.std.ListInstances
* @author Travis Brown
*/
trait JsonObjectOptics extends CatsConversions with ListInstances {
implicit val objectEach: Each[JsonObject, Json] = new Each[JsonObject, Json] {
implicit lazy val objectEach: Each[JsonObject, Json] = new Each[JsonObject, Json] {
def each: Traversal[JsonObject, Json] = new Traversal[JsonObject, Json] {
def modifyF[F[_]](f: Json => F[Json])(from: JsonObject)(implicit
F: Applicative[F]
): F[JsonObject] = from.traverse(f)(csApplicative(F))
}
}

implicit val objectAt: At[JsonObject, String, Json] = new At[JsonObject, String, Json]{
implicit lazy val objectAt: At[JsonObject, String, Json] = new At[JsonObject, String, Json]{
def at(field: String): Lens[JsonObject, Option[Json]] =
Lens[JsonObject, Option[Json]](_.apply(field))(optVal =>
obj => optVal.fold(obj - field)(value => obj + (field, value))
)
}

implicit val objectFilterIndex: FilterIndex[JsonObject, String, Json] =
implicit lazy val objectFilterIndex: FilterIndex[JsonObject, String, Json] =
new FilterIndex[JsonObject, String, Json] {
def filterIndex(p: String => Boolean) = new Traversal[JsonObject, Json] {
def modifyF[F[_]](f: Json => F[Json])(from: JsonObject)(implicit
Expand All @@ -44,5 +44,7 @@ trait JsonObjectOptics extends CatsConversions with ListInstances {
}
}

implicit val objectIndex: Index[JsonObject, String, Json] = Index.atIndex
implicit lazy val objectIndex: Index[JsonObject, String, Json] = Index.atIndex
}

object JsonObjectOptics extends JsonObjectOptics
26 changes: 15 additions & 11 deletions optics/src/main/scala/io/circe/optics/JsonOptics.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package io.circe.optics

import io.circe.{ Json, JsonNumber, JsonObject }
import io.circe.optics.JsonNumberOptics._
import monocle.Prism

/**
Expand All @@ -10,17 +11,20 @@ import monocle.Prism
* @author Travis Brown
*/
trait JsonOptics {
val jsonBoolean: Prism[Json, Boolean] = Prism[Json, Boolean](_.asBoolean)(Json.bool)
val jsonBigDecimal: Prism[Json, BigDecimal] = jsonNumber.composeIso(jsonNumberToBigDecimal)
val jsonDouble: Prism[Json, Double] = jsonNumber.composePrism(jsonNumberToDouble)
val jsonBigInt: Prism[Json, BigInt] = jsonNumber.composePrism(jsonNumberToBigInt)
val jsonLong: Prism[Json, Long] = jsonNumber.composePrism(jsonNumberToLong)
val jsonInt: Prism[Json, Int] = jsonNumber.composePrism(jsonNumberToInt)
val jsonShort: Prism[Json, Short] = jsonNumber.composePrism(jsonNumberToShort)
val jsonByte: Prism[Json, Byte] = jsonNumber.composePrism(jsonNumberToByte)
val jsonString: Prism[Json, String] = Prism[Json, String](_.asString)(Json.string)
val jsonNumber: Prism[Json, JsonNumber] =
lazy val jsonBoolean: Prism[Json, Boolean] = Prism[Json, Boolean](_.asBoolean)(Json.bool)
lazy val jsonBigDecimal: Prism[Json, BigDecimal] = jsonNumber.composeIso(jsonNumberToBigDecimal)
lazy val jsonDouble: Prism[Json, Double] = jsonNumber.composePrism(jsonNumberToDouble)
lazy val jsonBigInt: Prism[Json, BigInt] = jsonNumber.composePrism(jsonNumberToBigInt)
lazy val jsonLong: Prism[Json, Long] = jsonNumber.composePrism(jsonNumberToLong)
lazy val jsonInt: Prism[Json, Int] = jsonNumber.composePrism(jsonNumberToInt)
lazy val jsonShort: Prism[Json, Short] = jsonNumber.composePrism(jsonNumberToShort)
lazy val jsonByte: Prism[Json, Byte] = jsonNumber.composePrism(jsonNumberToByte)
lazy val jsonString: Prism[Json, String] = Prism[Json, String](_.asString)(Json.string)
lazy val jsonNumber: Prism[Json, JsonNumber] =
Prism[Json, JsonNumber](_.asNumber)(Json.fromJsonNumber)
val jsonObject: Prism[Json, JsonObject] =
lazy val jsonObject: Prism[Json, JsonObject] =
Prism[Json, JsonObject](_.asObject)(Json.fromJsonObject)
lazy val jsonArray: Prism[Json, List[Json]] = Prism[Json, List[Json]](_.asArray)(Json.fromValues)
}

object JsonOptics extends JsonOptics
27 changes: 27 additions & 0 deletions optics/src/main/scala/io/circe/optics/JsonPath.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.circe.optics

import io.circe.{ Decoder, Encoder, Json }
import io.circe.optics.JsonOptics._, io.circe.optics.JsonObjectOptics._
import monocle.{ Optional, Prism }
import monocle.function.index
import monocle.std.list._
import scala.language.dynamics

case class JsonPath(opt: Optional[Json, Json]) extends Dynamic {
def selectDynamic(field: String): JsonPath =
JsonPath(opt.composePrism(jsonObject).composeOptional(index(field)))

def at(i: Int): JsonPath =
JsonPath(opt.composePrism(jsonArray).composeOptional(index(i)))

def as[A](implicit decode: Decoder[A], encode: Encoder[A]): Optional[Json, A] =
opt.composePrism(
Prism((j: Json) => decode.decodeJson(j).toOption)(encode(_))
)

def `*`: Optional[Json, Json] = opt
}

object JsonPath {
val root: JsonPath = JsonPath(Optional.id)
}
8 changes: 7 additions & 1 deletion optics/src/main/scala/io/circe/optics/package.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
package io.circe

package object optics extends JsonOptics with JsonObjectOptics with JsonNumberOptics
package object optics {
def `*`: JsonPath = JsonPath.root
}

package optics {
object all extends JsonNumberOptics with JsonObjectOptics with JsonOptics
}

0 comments on commit 2ee2da9

Please sign in to comment.