Skip to content

Instantly share code, notes, and snippets.

@fokot
Last active December 26, 2015 17:49
Show Gist options
  • Save fokot/7189657 to your computer and use it in GitHub Desktop.
Save fokot/7189657 to your computer and use it in GitHub Desktop.
Expression and Json are typeclasses
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))
}
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