Skip to content

Instantly share code, notes, and snippets.

@shajra
Created March 10, 2016 05:47
Show Gist options
  • Save shajra/bd84c3d40002e008f92f to your computer and use it in GitHub Desktop.
Save shajra/bd84c3d40002e008f92f to your computer and use it in GitHub Desktop.
Example of using a tagless encoding (rough draft)
package shajra.tagless
import argonaut.{ HCursor, DecodeResult, DecodeJson, Parse }
import argonaut.Argonaut.jString
import scalaz._
import scalaz.Isomorphism.<~>
import scalaz.syntax.monad._
case class Field(name: FieldName, desc: FieldDesc)
case class FieldName(value: String) extends AnyVal
case class FieldDesc(value: String) extends AnyVal
case class IsPrimitive[A, E[_]](interp: E[A])
trait Rules[E[_]] {
def required[A](field: Field)(implicit ev: IsPrimitive[A, E]): E[A]
def optional[A](field: Field)(implicit ev: IsPrimitive[A, E]): E[Maybe[A]]
}
object Rules {
@inline def apply[E[_]](implicit r: Rules[E]): Rules[E] = r
}
case class Render[A](terms: IList[String])
object Render {
implicit val renderApply: Apply[Render] =
new Apply[Render] {
def ap[A, B](fa: => Render[A])(f: => Render[(A) => B]) =
Render[B](f.terms ++ fa.terms)
def map[A, B](fa: Render[A])(f: (A) => B) =
Render[B](fa.terms)
}
implicit val isPrimBool: IsPrimitive[Boolean, Render] =
IsPrimitive[Boolean, Render](Render[Boolean](IList("boolean")))
implicit val isPrimInt: IsPrimitive[Int, Render] =
IsPrimitive[Int, Render](Render[Int](IList("integer")))
implicit val rules: Rules[Render] =
// TODO: refactor copy/paste away
new Rules[Render] {
def required[A](field: Field)(implicit ev: IsPrimitive[A, Render]) =
Render[A](ev.interp.terms map { p =>
s"required: ${field.name.value}=${p} (${field.desc.value}})"
})
def optional[A](field: Field)(implicit ev: IsPrimitive[A, Render]) =
Render[Maybe[A]](ev.interp.terms map { p =>
s"optional: ${field.name.value}=${p} (${field.desc.value}})"
})
}
}
trait DecodeJsonImplicits {
type JsonReader[A] = Kleisli[DecodeResult, HCursor, A]
implicit val monad: Monad[DecodeJson] =
new IsomorphismMonad[DecodeJson, JsonReader] {
def G: Monad[JsonReader] = Monad[JsonReader]
def iso: DecodeJson <~> JsonReader =
new (DecodeJson <~> JsonReader) {
override def to: DecodeJson ~> JsonReader =
new (DecodeJson ~> JsonReader) {
override def apply[A](dj: DecodeJson[A]) = dj.kleisli
}
override def from: JsonReader ~> DecodeJson =
new (JsonReader ~> DecodeJson) {
override def apply[A](jr: JsonReader[A]) = DecodeJson(jr.run)
}
}
}
implicit def isPrim[A : DecodeJson]: IsPrimitive[A, DecodeJson] =
IsPrimitive[A, DecodeJson](implicitly[DecodeJson[A]])
implicit val rules: Rules[DecodeJson] =
// TODO: get Decode instances from IsPrimitive implicitly
new Rules[DecodeJson] {
def required[A]
(field: Field)(implicit ev: IsPrimitive[A, DecodeJson])
: DecodeJson[A] =
DecodeJson { _ --\ field.name.value as ev.interp }
def optional[A]
(field: Field)(implicit ev: IsPrimitive[A, DecodeJson])
: DecodeJson[Maybe[A]] =
DecodeJson
.OptionDecodeJson(DecodeJson { _ --\ field.name.value as ev.interp })
.map(Maybe.fromOption)
}
}
object Play extends App with DecodeJsonImplicits {
val knowsCrazy = Field(FieldName("knowsCrazy"), FieldDesc("don't know karate"))
val numPets = Field(FieldName("numPets"), FieldDesc("number of pets owned"))
def rule[E[_]: Rules : Apply]
(implicit boolIsPrim: IsPrimitive[Boolean, E],
intIsPrim: IsPrimitive[Int, E])
: E[(Boolean, Maybe[Int])] = {
val interp = Rules[E]
import interp._
(required[Boolean](knowsCrazy) |@| optional[Int](numPets)) { (b, i) => (b, i) }
}
println(rule[Render].terms)
println(Parse.decode("""{"knowsCrazy": true, "numPets": false}""")(rule[DecodeJson]))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment