Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Improve return type of 'merge' function using fundep encoding

  • Loading branch information...
commit 6eff05ed994131f6000dd805e0d959aa16d35a5c 1 parent de697c6
Joni Freeman authored
View
4 core/json/src/main/scala/net/liftweb/json/JsonAST.scala
@@ -29,10 +29,12 @@ object JsonAST {
*/
def concat(xs: JValue*) = xs.foldLeft(JNothing: JValue)(_ ++ _)
+ object JValue extends Merge.Mergeable
+
/**
* Data type for Json AST.
*/
- sealed abstract class JValue extends Merge.Mergeable with Diff.Diffable {
+ sealed abstract class JValue extends Diff.Diffable {
type Values
/** XPath-like expression to query JSON fields by name. Matches only fields on
View
67 core/json/src/main/scala/net/liftweb/json/Merge.scala
@@ -17,6 +17,43 @@
package net.liftweb
package json
+/** Use fundep encoding to improve return type of merge function
+ * (see: http://www.chuusai.com/2011/07/16/fundeps-in-scala/)
+ *
+ * JObject merge JObject = JObject
+ * JArray merge JArray = JArray
+ * _ merge _ = JValue
+ */
+private [json] trait MergeDep[A <: JValue, B <: JValue, R <: JValue] {
+ def apply(val1: A, val2: B): R
+}
+
+private [json] trait LowPriorityMergeDep {
+ implicit def jjj[A <: JValue, B <: JValue] = new MergeDep[A, B, JValue] {
+ def apply(val1: A, val2: B): JValue = merge(val1, val2)
+
+ private def merge(val1: JValue, val2: JValue): JValue = (val1, val2) match {
+ case (JObject(xs), JObject(ys)) => JObject(Merge.mergeFields(xs, ys))
+ case (JArray(xs), JArray(ys)) => JArray(Merge.mergeVals(xs, ys))
+ case (JField(n1, v1), JField(n2, v2)) if n1 == n2 => JField(n1, merge(v1, v2))
+ case (f1: JField, f2: JField) => f2
+ case (JNothing, x) => x
+ case (x, JNothing) => x
+ case (_, y) => y
+ }
+ }
+}
+
+private [json] trait MergeDeps extends LowPriorityMergeDep {
+ implicit object ooo extends MergeDep[JObject, JObject, JObject] {
+ def apply(val1: JObject, val2: JObject): JObject = JObject(Merge.mergeFields(val1.obj, val2.obj))
+ }
+
+ implicit object aaa extends MergeDep[JArray, JArray, JArray] {
+ def apply(val1: JArray, val2: JArray): JArray = JArray(Merge.mergeVals(val1.arr, val2.arr))
+ }
+}
+
/** Function to merge two JSONs.
*/
object Merge {
@@ -27,17 +64,10 @@ object Merge {
* m: JObject(List(JField(name,JString(joe)), JField(age,JInt(10)), JField(iq,JInt(105))))
* </pre>
*/
- def merge(val1: JValue, val2: JValue): JValue = (val1, val2) match {
- case (JObject(xs), JObject(ys)) => JObject(mergeFields(xs, ys))
- case (JArray(xs), JArray(ys)) => JArray(mergeVals(xs, ys))
- case (JField(n1, v1), JField(n2, v2)) if n1 == n2 => JField(n1, merge(v1, v2))
- case (f1: JField, f2: JField) => f2
- case (JNothing, x) => x
- case (x, JNothing) => x
- case (_, y) => y
- }
+ def merge[A <: JValue, B <: JValue, R <: JValue]
+ (val1: A, val2: B)(implicit instance: MergeDep[A, B, R]): R = instance(val1, val2)
- private def mergeFields(vs1: List[JField], vs2: List[JField]): List[JField] = {
+ private[json] def mergeFields(vs1: List[JField], vs2: List[JField]): List[JField] = {
def mergeRec(xleft: List[JField], yleft: List[JField]): List[JField] = xleft match {
case Nil => yleft
case JField(xn, xv) :: xs => yleft find (_.name == xn) match {
@@ -50,7 +80,7 @@ object Merge {
mergeRec(vs1, vs2)
}
- private def mergeVals(vs1: List[JValue], vs2: List[JValue]): List[JValue] = {
+ private[json] def mergeVals(vs1: List[JValue], vs2: List[JValue]): List[JValue] = {
def mergeRec(xleft: List[JValue], yleft: List[JValue]): List[JValue] = xleft match {
case Nil => yleft
case x :: xs => yleft find (_ == x) match {
@@ -62,10 +92,15 @@ object Merge {
mergeRec(vs1, vs2)
}
- private[json] trait Mergeable { this: JValue =>
- /** Return merged JSON.
- * @see net.liftweb.json.Merge#merge
- */
- def merge(other: JValue): JValue = Merge.merge(this, other)
+ private[json] trait Mergeable extends MergeDeps {
+ implicit def j2m[A <: JValue](json: A): MergeSyntax[A] = new MergeSyntax(json)
+
+ class MergeSyntax[A <: JValue](json: A) {
+ /** Return merged JSON.
+ * @see net.liftweb.json.Merge#merge
+ */
+ def merge[B <: JValue, R <: JValue](other: B)(implicit instance: MergeDep[A, B, R]): R =
+ Merge.merge(json, other)(instance)
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.