Upload
others
View
5
Download
0
Embed Size (px)
Citation preview
Juliano [email protected]
Shapeless in the real world:An introduction to generic programming.
Who am I?● Software Engineer,
Searcher of perfect modularization,Lover of Functional Languages
● The cool onesScala, Clojure, Elixir
● The "vintage" onesJava, C#, Python, Ruby
@vonjulianojuliano-alves.com
https://getquill.io
https://github.com/juliano/pdt-client
// 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] }
// 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()))
// 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(_)) }}
// ZIO Service
object HttpClient { // …
def http4s: URLayer[Has[Client[Task]], Has[Service]] = ZLayer.fromService[Client[Task], Service] { http4sClient: Client[Task] => Http4s(http4sClient) }}
Module Pattern
https://zio.dev/docs/howto/howto_use_layers
What is the problem?
// 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 clientedef siafi(request: OrgaoRequest): RIO[HttpClient, List[OrgaoSiafi]] = get[OrgaoSiafi]("orgaos-siafi", ???)
// 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)
// 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)
// 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")}
Show me the code
// 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 } }}
// 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] }
// domain.scala
case class OrgaoRequest(codigo: Option[String], descricao: Option[String], pagina: Int = 1) { def toMap: Map[String, String] = ???}
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", ???)}
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)}
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
Conclusion
Questions?
Juliano [email protected]
Shapeless in the real world:An introduction to generic programming.
Thank you!