shapeless-guide
shapeless-guide copied to clipboard
Section 5 needs update regarding `Lazy`?
As far as I recall, there was a relatively recent SIP that improved the implicit resolution process, making the usage of Lazy
redundant in at least some cases (quick research suggests this was before 2.12.0
).
It would be perhaps advantageous to take it into account as its one less concept to hold in ones head when reading the book, for a number of code snippets. For example, here's a (somewhat modified, mostly replaced explicit trait instance creation with SAM resolution) an example for the code necessary for 5.3 that compiles without using Lazy
:
import shapeless.labelled.FieldType
import shapeless.{::, HList, HNil, LabelledGeneric, Witness}
sealed trait JsonValue
case class JsonObject(fields: List[(String, JsonValue)]) extends JsonValue
case class JsonArray(items: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: Double) extends JsonValue
case class JsonBoolean(value: Boolean) extends JsonValue
case object JsonNull extends JsonValue
trait JsonEncoder[A] {
def encode(value: A): JsonValue
}
object JsonEncoder {
def apply[A](implicit enc: JsonEncoder[A]): JsonEncoder[A] = enc
implicit val stringEncoder: JsonEncoder[String] = JsonString.apply
implicit val doubleEncoder: JsonEncoder[Double] = JsonNumber.apply
implicit val intEncoder: JsonEncoder[Int] = i => JsonNumber(i)
implicit val booleanEncoder: JsonEncoder[Boolean] = JsonBoolean.apply
implicit def listEncoder[A](
implicit enc: JsonEncoder[A]): JsonEncoder[List[A]] =
l => JsonArray(l.map(enc.encode))
implicit def optionEncoder[A](
implicit enc: JsonEncoder[A]): JsonEncoder[Option[A]] =
_.map(enc.encode).getOrElse(JsonNull)
}
trait JsonObjectEncoder[A] extends JsonEncoder[A] {
def encode(value: A): JsonObject
}
object JsonObjectEncoder {
implicit val hnilEncoder: JsonObjectEncoder[HNil] = _ => JsonObject(Nil)
implicit def hlistObjectEncoder[K <: Symbol, H, T <: HList](
implicit fieldKey: Witness.Aux[K],
hEncoder: JsonEncoder[H], //Lazy was here
tEncoder: JsonObjectEncoder[T])
: JsonObjectEncoder[FieldType[K, H] :: T] = {
val fieldName = fieldKey.value.name
{
case (head :: tail) =>
JsonObject(
(fieldName -> hEncoder.encode(head)) :: tEncoder.encode(tail).fields)
}
}
implicit def genericObjectEncoder[A, H](
implicit generic: LabelledGeneric.Aux[A, H],
hListEncoder: JsonObjectEncoder[H]): JsonEncoder[A] = //and here
(o: A) => hListEncoder.encode(generic.to(o))
}
case class IceCream(name: String, numCherries: Int, inCone: Boolean)
object test extends App {
import JsonObjectEncoder._
val iceCream = IceCream("Sundae", 1, inCone = false)
println(JsonEncoder[IceCream].encode(iceCream))
}