Last active
December 26, 2015 17:49
-
-
Save fokot/7189657 to your computer and use it in GitHub Desktop.
Expression and Json are typeclasses
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package json | |
sealed trait JsonValue | |
case class JsonObject(entries: Map[String, JsonValue]) extends JsonValue | |
case class JsonArray(entries: Seq[JsonValue]) extends JsonValue | |
case class JsonString(value: String) extends JsonValue | |
case class JsonNumber(value: BigDecimal) extends JsonValue | |
case class JsonBoolean(value: Boolean) extends JsonValue | |
case object JsonNull extends JsonValue | |
trait Json[A] { | |
def json(value: A): JsonValue | |
} | |
object JsonWriter { | |
def write(value: JsonValue): String = value match { | |
case JsonObject(entries) => | |
"{" + (for ((key, value) <- entries) yield key + ": " + write(value)) + "}" | |
case JsonArray(entries) => | |
"[" + (entries map write) mkString ", " + "]" | |
case JsonString(value) => "\"" + value + "\"" | |
case JsonNumber(value) => value.toString | |
case JsonBoolean(value) => value.toString | |
case JsonNull => "null" | |
} | |
def write[A](value: A)(implicit conv: Json[A]): String = write(conv.json(value)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package typeclasses | |
import json._ | |
trait Expression[A] { | |
def value(expression: A): Int | |
} | |
case class Number(value: Int) | |
case class Plus[A, B](lhs: A, rhs: B) | |
case class Minus[A, B](lhs: A, rhs: B) | |
object App extends App { | |
implicit def addJson[A](a : A)(implicit json: Json[A]) = new { def toJson = JsonWriter write a } | |
implicit def addExpression[A](a : A)(implicit exp: Expression[A]) = new { def value = exp.value(a) } | |
implicit object NumberExpression extends Expression[Number] { | |
def value(n: Number) = n.value | |
} | |
implicit def plusExpression[A : Expression, B : Expression] = | |
new Expression[Plus[A, B]] { | |
def value(p: Plus[A, B]) = implicitly[Expression[A]].value(p.lhs) + | |
implicitly[Expression[B]].value(p.rhs) | |
} | |
implicit def minusExpression[A : Expression, B : Expression] = | |
new Expression[Minus[A, B]] { | |
def value(p: Minus[A, B]) = implicitly[Expression[A]].value(p.lhs) - | |
implicitly[Expression[B]].value(p.rhs) | |
} | |
implicit object NumberJson extends Json[Number] { | |
def json(n: Number) = JsonNumber(n.value) | |
} | |
implicit def plusJson[A : Json, B : Json] = | |
new Json[Plus[A, B]] { | |
def json(p: Plus[A, B]) = JsonObject( | |
Map("op" -> JsonString("+"), | |
"lhs" -> implicitly[Json[A]].json(p.lhs), | |
"rhs" -> implicitly[Json[B]].json(p.rhs) | |
)) | |
} | |
implicit def minusJson[A : Json, B : Json] = | |
new Json[Minus[A, B]] { | |
def json(p: Minus[A, B]) = JsonObject( | |
Map("op" -> JsonString("-"), | |
"lhs" -> implicitly[Json[A]].json(p.lhs), | |
"rhs" -> implicitly[Json[B]].json(p.rhs) | |
)) | |
} | |
implicit object StringJson extends Json[String] { | |
def json(value: String) = JsonString(value) | |
} | |
implicit def pairToExpression[A: Expression, B: Expression] = { | |
new Expression[(A, B)] { | |
def value(pp: (A, B)) = pp._1.value * pp._2.value | |
} | |
} | |
implicit object StringPairJson extends Json[(String, String)] { | |
def json(value: (String, String)) = JsonString("This is a string pair") | |
} | |
implicit def pairToJson[A: Json, B: Json] = { | |
new Json[(A, B)] { | |
def json(pp: (A, B)) = JsonObject(Map("1" -> implicitly[Json[A]].json(pp._1), "2" -> implicitly[Json[B]].json(pp._2))) | |
} | |
} | |
println("value is : " + Plus(Number(2), Minus(Number(3), Number((2)))).value ) | |
println("json is : " + Plus(Number(2), Minus(Number(3), Number((2)))).toJson ) | |
// pair also works we created typeclasses | |
println("pair value is : " + (Plus(Number(2), Number(3)), Number(3)).value ) | |
println("pair json is : " + (Plus(Number(2), Number(3)), Number(3)).toJson ) | |
// we need to pass Json explicitly because its ambiguous | |
println("pair json is : " + JsonWriter.write(("first", "second"))(StringPairJson) ) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment