shapeless-guide icon indicating copy to clipboard operation
shapeless-guide copied to clipboard

Section 5 needs update regarding `Lazy`?

Open mikolak-net opened this issue 7 years ago • 0 comments

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))

}

mikolak-net avatar Sep 08 '17 16:09 mikolak-net