Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shapeless in the real world: An introduction to generic programming

Shapeless in the real world: An introduction to generic programming

Shapeless is a powerful Scala library used behind many others, such as Circe or Doobie. This talk will show you how shapeless works, using it to solve a real problem.

Juliano Alves

May 06, 2020
Tweet

More Decks by Juliano Alves

Other Decks in Technology

Transcript

  1. Juliano Alves
    @vonjuliano
    juliano-alves.com
    Shapeless in the real world:
    An introduction to generic
    programming.

    View Slide

  2. Who am I?
    ● Software Engineer,
    Searcher of perfect modularization,
    Lover of Functional Languages
    ● The cool ones
    Scala, Clojure, Elixir
    ● The "vintage" ones
    Java, C#, Python, Ruby
    @vonjuliano
    juliano-alves.com

    View Slide

  3. https://getquill.io

    View Slide

  4. View Slide

  5. https://github.com/juliano/pdt-client

    View Slide

  6. // ZIO Service
    object HttpClient {
    type HttpClient = Has[Service]
    trait Service {
    protected final val rootUrl =
    "http://www.transparencia.gov.br/api-de-dados/"
    def get[T](uri: String, parameters: Map[String, String])
    (implicit d: Decoder[T]): Task[T]
    }

    View Slide

  7. // ZIO Service
    // Helpers
    def get[T](resource: String,
    parameters: Map[String, String] = Map())
    (implicit d: Decoder[T]): RIO[HttpClient, List[T]] =
    RIO.accessM[HttpClient](
    _.get.get[List[T]](resource, parameters))
    def get[T](resource: String, id: Long)
    (implicit d: Decoder[T]): RIO[HttpClient, T] =
    RIO.accessM[HttpClient](
    _.get.get[T](s"$resource/$id", Map()))

    View Slide

  8. // Http4s Implementation
    private[client] final case class Http4s(client: Client[Task])
    extends HttpClient.Service with Http4sClientDsl[Task] {
    def get[T](resource: String,
    parameters: Map[String, String])
    (implicit d: Decoder[T]): Task[T] = {
    val uri = Uri(path = rootUrl + resource)
    .withQueryParams(parameters)
    client
    .expect[T](uri.toString())
    .foldM(IO.fail(_), ZIO.succeed(_))
    }
    }

    View Slide

  9. // ZIO Service
    object HttpClient {
    // …
    def http4s: URLayer[Has[Client[Task]], Has[Service]] =
    ZLayer.fromService[Client[Task], Service] {
    http4sClient: Client[Task] => Http4s(http4sClient)
    }
    }
    Module Pattern

    View Slide

  10. https://zio.dev/docs/howto/howto_use_layers

    View Slide

  11. What is the problem?

    View Slide

  12. // ZIO Service
    object HttpClient {
    type HttpClient = Has[Service]
    trait Service {
    protected final val rootUrl =
    "http://www.transparencia.gov.br/api-de-dados/"
    def get[T](uri: String, parameters: Map[String, String])
    (implicit d: Decoder[T]): Task[T]
    }
    // Algum cliente
    def siafi(request: OrgaoRequest):
    RIO[HttpClient, List[OrgaoSiafi]] =
    get[OrgaoSiafi]("orgaos-siafi", ???)

    View Slide

  13. // domain.scala
    case class OrgaoRequest(codigo: Option[String],
    descricao: Option[String],
    pagina: Int = 1)
    case class OrgaoSiafi(codigo: String,
    codigoDescricaoFormatado: String, descricao: String)
    case class OrgaoSiape(codigo: String,
    codigoDescricaoFormatado: String, descricao: String)

    View Slide

  14. // domain.scala
    case class OrgaoRequest(codigo: Option[String],
    descricao: Option[String],
    pagina: Int = 1) {
    def toMap: Map[String, String] = ???
    }
    case class OrgaoSiafi(codigo: String,
    codigoDescricaoFormatado: String, descricao: String)
    case class OrgaoSiape(codigo: String,
    codigoDescricaoFormatado: String, descricao: String)

    View Slide

  15. View Slide

  16. // domain.scala
    case class OrgaoRequest(codigo: Option[String],
    descricao: Option[String],
    pagina: Int = 1) {
    def toMap: Map[String, String] =
    Map("codigo" -> "1337",
    "descricao" -> "Orgão Federal da Carreta Furacão",
    "pagina" -> "1")
    }

    View Slide

  17. View Slide

  18. Show me the code

    View Slide

  19. // implicits.scala
    package object implicits {
    implicit class HttpRequestOps[A <: Product](val a: A) {
    def parameters(implicit toMap: ToMap.Aux[A, Symbol, Any]):
    Map[String, String] =
    a.toMap[Symbol, Any]
    .filter {
    case (_, v: Option[Any]) => v.isDefined
    case (_, v) => v != null
    }
    .map {
    case (k, v: Option[Any]) => k.name -> v.get.toString
    case (k, v) => k.name -> v.toString
    }
    }
    }

    View Slide

  20. // ZIO Service
    object HttpClient {
    type HttpClient = Has[Service]
    trait Service {
    protected final val rootUrl =
    "http://www.transparencia.gov.br/api-de-dados/"
    def get[T](uri: String, parameters: Map[String, String])
    (implicit d: Decoder[T]): Task[T]
    }

    View Slide

  21. // domain.scala
    case class OrgaoRequest(codigo: Option[String],
    descricao: Option[String],
    pagina: Int = 1) {
    def toMap: Map[String, String] = ???
    }

    View Slide

  22. object Orgaos {
    def siafi(request: OrgaoRequest):
    RIO[HttpClient, List[OrgaoSiafi]] =
    get[OrgaoSiafi]("orgaos-siafi", ???)
    }
    object CEISs {
    def by(request: CEISRequest):
    RIO[HttpClient, List[CEIS]] =
    get[CEIS]("ceis", ???)
    }

    View Slide

  23. import pdt.implicits.HttpRequestOps
    object Orgaos {
    def siafi(request: OrgaoRequest):
    RIO[HttpClient, List[OrgaoSiafi]] =
    get[OrgaoSiafi]("orgaos-siafi",
    HttpRequestOps(request).parameters)
    }
    object CEISs {
    def by(request: CEISRequest):
    RIO[HttpClient, List[CEIS]] =
    get[CEIS]("ceis", HttpRequestOps(request).parameters)
    }

    View Slide

  24. More about shapeless
    ● https://juliano-alves.com/2020/04/06/shapeless-a-real-world-use-case/
    The post which inspired this talk
    ● https://underscore.io/books/shapeless-guide/
    The Type Astronaut's guide to shapeless

    View Slide

  25. Conclusion

    View Slide

  26. Questions?

    View Slide

  27. Juliano Alves
    @vonjuliano
    juliano-alves.com
    Shapeless in the real world:
    An introduction to generic
    programming.
    Thank you!

    View Slide