Translate

viernes, 1 de abril de 2022

Funtores

Los funtores son una abstracción que nos permite representar secuencias de operaciones dentro de un contexto como una Lista, un Option o cualquiera de miles de otras posibilidades. Los funtores por sí solos no son tan útiles, pero los casos especiales de funtores, como las mónadas y los funtores aplicativos, son algunas de las abstracciones más utilizadas.

Informalmente, un funtor es cualquier cosa con un método map. Probablemente conozca muchos tipos que tienen esto: Option, List, y Either, por nombrar algunos. Por lo general, nos encontramos primero con el map cuando iteramos sobre Listas. Sin embargo, para entender los funtores necesitamos pensar en el método de otra manera. En lugar de recorrer la lista, deberíamos pensar en ella como transformar todos los valores dentro de una sola vez. Especificamos la función que se va a aplicar y el map se asegura de que se aplique a cada elemento. Los valores cambian pero la estructura de la lista (el número de elementos y su orden) permanece igual:

List(1, 2, 3).map(n => n + 1)

// res0: List[Int] = List(2, 3, 4)

De manera similar, cuando asignamos una opción, transformamos los contenidos pero dejamos el contexto Some o None sin cambios. El mismo principio se aplica a Either con sus contextos Left y Right. Esta noción general de transformación, junto con el patrón común de firmas de tipo, es lo que conecta el comportamiento del mapa en diferentes tipos de datos.



Debido a que map deja la estructura del contexto sin cambios, podemos llamarlo repetidamente para secuenciar múltiples cálculos sobre el contenido de una estructura de datos inicial:

List(1, 2, 3).
map(n => n + 1).
map(n => n * 2).
map(n => s"${n}!")
// res1: List[String] = List("4!", "6!", "8!")

Deberíamos pensar en el map no como un patrón de iteración, sino como una forma de secuenciar cálculos sobre valores ignorando algunas complicaciones dictadas por el tipo de datos relevante:



[eBook] Cloud Native DevOps with Kubernetes, 2nd Edition

 

  • https://interact.f5.com/rs/653-SMC-783/images/EBK - Cloud Native DevOps with Kubernetes 2nd Edition - 760x284.png

    Hi Emanuel,

    Kubernetes is the operating system of the cloud-native world, providing a reliable and scalable platform for running containerized workloads. In this comprehensive eBook, cloud experts Justin Domingus and John Arundel show your development and operations staff what Kubernetes can do – and what you can do with it.

    This updated second edition, provided with compliments of F5 NGINX, guides you through the growing Kubernetes ecosystem and provides practical solutions to everyday problems using tools currently in use in the software industry. You'll walk through a sample containerized application running in Kubernetes step-by-step, from the development environment through the continuous deployment pipeline, with patterns you can use for your own applications. Make your development teams lean, fast, and effective by adopting Kubernetes and DevOps principles.

    In this eBook you will learn:

    • About containers and Kubernetes from first principles – no experience necessary – and how to design your own cloud‑native services and infrastructure
    • How to run your own clusters, or choose a managed Kubernetes service from Amazon, Google, and others, while managing resource usage and the container lifecycle
    • How to optimize clusters for cost, performance, resilience, capacity, and scalability while using the best tools for developing, testing, and deploying your applications
    • How to apply the latest industry practices for observability, monitoring, and security in production


jueves, 31 de marzo de 2022

Monoides en Cats parte 3


Seguimos con monoides en cats.

Recordemos : 

• un Semigrupo representa una operación de suma o combinación;

• un monoide extiende un semigrupo agregando una identidad o un elemento "cero".

Podemos usar semigrupos y monoides importando tres cosas: las type class en sí, las instancias de los tipos que nos interesan y la sintaxis de semigrupo para darnos el operador |+| :

import cats.Monoid

import cats.instances.string._ // for Monoid

import cats.syntax.semigroup._ // for |+|

"Scala" |+| " with " |+| "Cats"

// res0: String = "Scala with Cats"

Con las instancias correctas en el alcance, podemos comenzar a agregar lo que queramos:

import cats.instances.int._ // for Monoid
import cats.instances.option._ // for Monoid

Option(1) |+| Option(2)
// res1: Option[Int] = Some(3)

import cats.instances.map._ // for Monoid

val map1 = Map("a" -> 1, "b" -> 2)
val map2 = Map("b" -> 3, "d" -> 4)
map1 |+| map2
// res2: Map[String, Int] = Map("b" -> 5, "d" -> 4, "a" -> 1)

import cats.instances.tuple._  // for Monoid
val tuple1 = ("hello", 123)
val tuple2 = ("world", 321)
tuple1 |+| tuple2
// res3: (String, Int) = ("helloworld", 444)

También podemos escribir código genérico que funcione con cualquier tipo para el que tengamos una instancia de Monoid:

def addAll[A](values: List[A])
(implicit monoid: Monoid[A]): A =
values.foldRight(monoid.empty)(_ |+| _)

addAll(List(1, 2, 3))
// res4: Int = 6

addAll(List(None, Some(1), Some(2)))
// res5: Option[Int] = Some(3)

Los monoides son una gran puerta de entrada a Cats. Son fáciles de entender y fáciles de usar. Sin embargo, son solo la punta del iceberg en términos de las abstracciones que Cats nos permite hacer. 

miércoles, 30 de marzo de 2022

Monoides ¿dónde son útiles?

Ahora sabemos qué es un monoide, una abstracción del concepto de sumar o combinar, pero ¿dónde es útil? Aquí hay algunas grandes ideas donde los monoides juegan un papel importante. 

Big Data : En aplicaciones de big data como Spark y Hadoop, distribuimos el análisis de datos en muchas máquinas, brindando tolerancia a fallas y escalabilidad. Esto significa que cada máquina devolverá resultados sobre una parte de los datos, y luego debemos combinar estos resultados para obtener nuestro resultado final. En la gran mayoría de los casos, esto puede verse como un monoide.

Si queremos calcular cuántos visitantes totales ha recibido un sitio web, eso significa calcular un Int en cada parte de los datos. Sabemos que la instancia monoide de Int es la suma, que es la forma correcta de combinar resultados parciales.

Si queremos saber cuántos visitantes únicos ha recibido un sitio web, eso es equivalente a construir un Set [Usuario] en cada parte de los datos. Sabemos que la instancia monoide de Set es la unión de conjuntos, que es la forma correcta de combinar resultados parciales.

Si queremos calcular tiempos de respuesta del 99 % y 95 % a partir de los registros de nuestro servidor, podemos usar una estructura de datos llamada QTree para la cual existe un monoide.

Casi todos los análisis que podríamos querer hacer sobre un gran conjunto de datos son monoides y, por lo tanto, podemos construir un sistema de análisis expresivo y poderoso en torno a esta idea. Esto es exactamente lo que han hecho los proyectos Algebird y Summingbird de Twitter. 

Sistemas distribuidos: En un sistema distribuido, diferentes máquinas pueden terminar con diferentes vistas de datos. Por ejemplo, una máquina puede recibir una actualización que otras máquinas no recibieron. Nos gustaría conciliar estas diferentes vistas, de modo que cada máquina tenga los mismos datos si no llegan más actualizaciones. Esto se llama consistencia eventual.

Una clase particular de tipos de datos admite esta reconciliación. Estos tipos de datos se denominan tipos de datos replicados conmutativos (CRDT). La operación clave es la capacidad de fusionar dos instancias de datos, con un resultado que captura toda la información en ambas instancias. Esta operación se basa en tener una instancia monoide.

Monoides en Cats parte 2


Seguimos con monoides en cats.

Cats proporciona sintaxis en forma de |+| para el método combine

Debido a que combine técnicamente proviene de Semigroup, accedemos a la sintaxis importando desde cats.syntax.semigroup:

import cats.instances.string._ // for Monoid

import cats.syntax.semigroup._ // for |+|

val stringResult = "Hi " |+| "there" |+| Monoid[String].empty

// stringResult: String = "Hi there"

import cats.instances.int._ // for Monoid

val intResult = 1 |+| 2 |+| Monoid[Int].empty
// intResult: Int = 3


lunes, 28 de marzo de 2022

Monoides en Cats


El tipe class monoid es cats.kernel.Monoid, que tiene el alias cats.Monoid. Monoid extiende cats.kernel.Semigroup, que tiene el alias de cats.Semigroup. Cuando usamos Cats, normalmente importamos estas clases : 

import cats.Monoid

import cats.Semigroup


Monoid sigue el patrón Cats estándar: el objeto complementario tiene un método de aplicación que devuelve la instancia del type class para un tipo en particular. Por ejemplo, si queremos la instancia monoide para String y tenemos los implícitos correctos en el alcance, podemos escribir lo siguiente:

import cats.Monoid
import cats.instances.string._ // for Monoid

Monoid[String].combine("Hi ", "there")
// res0: String = "Hi there"
Monoid[String].empty
// res1: String = ""

que es equivalente a:

Monoid.apply[String].combine("Hi ", "there")
// res2: String = "Hi there"
Monoid.apply[String].empty
// res3: String = ""

Como sabemos, Monoid extiende Semigroup. Si no necesitamos empty, podemos escribir de manera equivalente:

import cats.Semigroup
Semigroup[String].combine("Hi ", "there")
// res4: String = "Hi there"

Las instancias de clase de tipo para Monoid se organizan en cats.instances de la manera estándar. Por ejemplo, si queremos obtener instancias para Int, las importamos cats.instances.int:

import cats.Monoid
import cats.instances.int._ // for Monoid

Monoid[Int].combine(32, 10)
// res5: Int = 42

De manera similar, podemos ensamblar un Monoid[Option[Int]] usando instancias de cats.instances.int y cats.instances.option:

import cats.Monoid
import cats.instances.int._
 // for Monoid
import cats.instances.option._ // for Monoid

val a = Option(22)
// a: Option[Int] = Some(22)
val b = Option(20)
// b: Option[Int] = Some(20)
Monoid[Option[Int]].combine(a, b)
// res6: Option[Int] = Some(42)

Como siempre, a menos que tengamos una buena razón para importar instancias individuales, podemos importar todo.

import cats._
import cats.implicits._

lunes, 21 de marzo de 2022

Monoides y Semigrupos parte 2

Seguimos con Monoides y Semigrupos. 

Un semigrupo es solo la parte combinada de un monoide, sin elemento vacio.

Si bien muchos semigrupos también son monoides, hay algunos tipos de datos para los que no podemos definir un elemento vacío. Por ejemplo, acabamos de ver que la concatenación de secuencias y la suma de enteros son monoides. Sin embargo, si nos restringimos a secuencias no vacías y enteros positivos, ya no podemos definir un elemento vacío sensible. Cats tiene un tipo de datos NonEmptyList que tiene una implementación de Semigroup pero no una implementación de Monoide.

Una definición más precisa (aunque todavía simplificada) de Cats' Monoid es:

trait Semigroup[A] {

  def combine(x: A, y: A): A

}

trait Monoid[A] extends Semigroup[A] {

  def empty: A

}

Veremos este tipo de herencia a menudo cuando discutamos las clases de tipos. Proporciona modularidad y nos permite reutilizar el comportamiento. Si definimos un Monoide para un tipo A, obtenemos un Semigrupo gratis. De manera similar, si un método requiere un parámetro de tipo Semigroup[B], podemos pasar un Monoid[B] en su lugar.


Apache Isis

 


Apache Isis permite el desarrollo de aplicaciones basadas en domain-driven al generar automáticamente una interfaz de usuario para sus aplicaciones Spring Boot. Eso significa:

  • Productividad: la aplicación es principalmente lógica comercial.
  • Descubrimiento: los expertos en el dominio comercial y los técnicos pueden trabajar de la mano para comprender el espacio del problema y luego el espacio de la solución.
  • Facilidad de uso: los usuarios comerciales encontrarán que la aplicación es fácil de aprender, ya que los conceptos comerciales están al frente y al centro.
  • Arquitectura limpia: el marco garantiza la separación de responsabilidades dentro de la arquitectura interna de su aplicación
  • Integración simple: publica automáticamente un evento de dominio genérico para integraciones asíncronas y una API REST para integración de sincronizas.
  • Y debido a que Apache Isis es de código abierto, se basa en estándares industriales/de facto como Spring Boot.


Apache Superset


Apache Superset es una plataforma moderna de exploración y visualización de datos.

Apache Superset es rápido, liviano, intuitivo y está repleto de opciones que facilitan a los usuarios de todos los conjuntos de habilidades explorar y visualizar sus datos, desde simples gráficos circulares hasta gráficos geoespaciales deck.gl altamente detallados.

Apache Superset  proporciona:

  • Una interfaz intuitiva para visualizar conjuntos de datos y crear paneles interactivos
  • Una amplia gama de hermosas visualizaciones para mostrar sus datos
  • Generador de visualización sin código para extraer y presentar conjuntos de datos
  • Un IDE de SQL para preparar datos para visualización, incluido un navegador de metadatos enriquecido
  • Una capa semántica ligera que permite a los analistas de datos definir rápidamente dimensiones y métricas personalizadas
  • Soporte listo para usar para la mayoría de las bases de datos que hablan SQL
  • Almacenamiento en caché y consultas asincrónicas en la memoria sin inconvenientes
  • Un modelo de seguridad extensible que permite la configuración de reglas muy complejas sobre quién puede acceder a qué funciones y conjuntos de datos del producto.
  • Integración con los principales backends de autenticación (base de datos, OpenID, LDAP, OAuth, REMOTE_USER, etc.)
  • La capacidad de agregar complementos de visualización personalizados
  • Una API para la personalización programática
  • Una arquitectura nativa de la nube diseñada desde cero para escalar

Superset es nativo de la nube y está diseñado para tener una alta disponibilidad. Fue diseñado para escalar a grandes entornos distribuidos y funciona muy bien dentro de contenedores. Si bien puede probar fácilmente Superset en una configuración modesta o simplemente en su computadora portátil, prácticamente no hay límite para escalar la plataforma.

Superset también es nativo de la nube en el sentido de que es flexible y le permite elegir:

  • servidor web (Gunicorn, Nginx, Apache),
  • motor de base de datos de metadatos (MySQL, Postgres, MariaDB, etc.),
  • cola de mensajes (Redis, RabbitMQ, SQS, etc.),
  • backend de resultados (S3, Redis, Memcached, etc.),
  • capa de almacenamiento en caché (Memcached, Redis, etc.),
  • Superset también funciona bien con servicios como NewRelic, StatsD y DataDog, y tiene la capacidad de ejecutar cargas de trabajo analíticas contra las tecnologías de bases de datos más populares.

Superset se ejecuta actualmente a escala en muchas empresas. Por ejemplo, Superset se ejecuta en el entorno de producción de Airbnb dentro de Kubernetes y sirve a más de 600 usuarios activos diarios que ven más de 100 000 gráficos al día.

Sin más dejo link: https://superset.apache.org/

viernes, 18 de marzo de 2022

Google Java App Engine Standard es open source


Google ha abierto el código fuente de Java para el entorno estándar de Google App Engine, el runtime, las API de App Engine y el SDK.

Lanzado inicialmente en 2018, Google App Engine se diseñó para facilitar a los desarrolladores la implementación y el escalado de sus aplicaciones web. App Engine actualmente es compatible con muchos lenguajes como: Java, PHP, Python, Node.js, Go, Ruby.

Los desarrolladores de Java pueden implementar aplicaciones web basadas en servlets, utilizando Java 8, Java 11 y Java 17, así como otros lenguajes JVM como Groovy y Kotlin. Además, es posible utilizar muchos frameworks, como Spring Boot, Quarkus, Vert.x y Micronaut.

Queria compartir con ustedes esta noticia, que más que nada me llamo la atención. Dado que no conozco gente que este usando App Engine y me parece que estos esfuerzos para hacer la plataforma más popular llegaron tarde. 

Dejo link : https://cloud.google.com/blog/topics/developers-practitioners/open-sourcing-app-engine-standard-java-runtime


lunes, 14 de marzo de 2022

Monoides y Semigrupos

La adición de Ints es una operación binaria cerrada, lo que significa que sumar dos Ints siempre produce otro Int:

2 + 1

// res0: Int = 3

También existe el elemento de identidad 0 con la propiedad de que a + 0 == 0 + a== a para cualquier Int a:

2 + 0
// res1: Int = 2
0 + 2
// res2: Int = 2

También hay otras propiedades de la adición. Por ejemplo, no importa en qué orden agreguemos elementos porque siempre obtenemos el mismo resultado. Esta es una propiedad conocida como asociatividad:

(1 + 2) + 3
// res3: Int = 6
1 + (2 + 3)
// res4: Int = 6

Las mismas propiedades para la suma también se aplican para la multiplicación, siempre que usemos 1 como identidad en lugar de 0:

1 * 3
// res5: Int = 3
3 * 1
// res6: Int = 3

La multiplicación, como la suma, es asociativa:

(1 * 2) * 3
// res7: Int = 6
1 * (2 * 3)
// res8: Int = 6

También podemos agregar Cadenas, usando la concatenación de cadenas como nuestro operador binario:

"One" ++ "two"
// res9: String = "Onetwo"

y la cadena vacía como la identidad:

"" ++ "Hello"
// res10: String = "Hello"
"Hello" ++ ""
// res11: String = "Hello"

Una vez más, la concatenación es asociativa:

("One" ++ "Two") ++ "Three"
// res12: String = "OneTwoThree"
"One" ++ ("Two" ++ "Three")
// res13: String = "OneTwoThree"

Tenga en cuenta que usamos ++ arriba en lugar del + más habitual para sugerir un paralelo con secuencias. Podemos hacer lo mismo con otros tipos de secuencias, usando la concatenación como operador binario y la secuencia vacía como nuestra identidad.

Formalmente, un monoide para un tipo A es:

• una operación o función que permite "combinar" dos elementos y retornar uno del mismo tipo, la firma sería : (A, A) => A
• un elemento vacío de tipo A

Esta definición se traduce muy bien en código Scala. Aquí hay una versión simplificada de la definición de Cats:

trait Monoid[A] {

   def combine(x: A, y: A): A

   def empty: A

}

Además de proporcionar las operaciones de combinar y vaciar, los monoides deben obedecer formalmente varias leyes. Para todos los valores x, y y z, en A, la combinación debe ser asociativa y el vacío debe ser un elemento de identidad:

def associativeLaw[A](x: A, y: A, z: A)

   (implicit m: Monoid[A]): Boolean = {

    m.combine(x, m.combine(y, z)) ==

    m.combine(m.combine(x, y), z)

}

def identityLaw[A](x: A)

   (implicit m: Monoid[A]): Boolean = {

   (m.combine(x, m.empty) == x) &&

   (m.combine(m.empty, x) == x)

}

La resta de enteros, por ejemplo, no es un monoide porque la resta no es asociativa:

(1 - 2) - 3
// res14: Int = -4

1 - (2 - 3)
// res15: Int = 2

En la práctica, solo necesitamos pensar en las leyes cuando escribimos nuestras propias instancias de Monoid. Las instancias ilegales son peligrosas porque pueden producir resultados impredecibles cuando se usan con el resto de la maquinaria de Cats. La mayoría de las veces podemos confiar en las instancias proporcionadas por Cats y asumir que los autores de la biblioteca saben lo que están haciendo.

viernes, 11 de marzo de 2022

Libros gratuitos de webcodegeeks

 

Download IT Guides!

 

The Best Web Programming Languages to Learn

A more comprehensive list of tasks to which web development commonly refers, may include web engineering, web design, web content development, client liaison, client-side/server-side...

 
 

Web Developer Interview Questions

A more comprehensive list of tasks to which web development commonly refers, may include web engineering, web design, web content development, client liaison, client-side/server-side...

 
 

Git Tutorial

Git is, without any doubt, the most popular version control system. Ironically, there are other version control systems easier to learn and to use, but, despite that, Git is the favorite...

 
 

Docker Containerization Cookbook

Docker is the world's leading software containerization platform. Docker containers wrap a piece of software in a complete filesystem that contains everything needed to run: code,...

 

jueves, 10 de marzo de 2022

Empezando con Cats parte 3


 Al trabajar con type class, debemos tener en cuenta dos cuestiones que controlan la selección de instancias:

  • ¿Cuál es la relación entre una instancia definida en un tipo y sus subtipos? Por ejemplo, si definimos un JsonWriter[Option[Int]], ¿la expresión Json.toJson(Some(1)) seleccionará esta instancia? (Recuerde que Some es un subtipo de Option).
  • ¿Cómo elegimos entre instancias de type class cuando hay muchas disponibles? ¿Qué pasa si definimos dos JsonWriters para Person? Cuando escribimos Json.toJson(aPerson), ¿qué instancia se selecciona?

Cuando definimos clases de tipo, podemos agregar anotaciones de variación al parámetro de tipo para afectar la variación de la clase de tipo y la capacidad del compilador para seleccionar instancias durante la resolución implícita.

La varianza se relaciona con los subtipos. Decimos que B es un subtipo de A si podemos usar un valor de tipo B en cualquier lugar donde esperamos un valor de tipo A.

Las anotaciones de covarianza y contravarianza surgen cuando se trabaja con constructores de tipos. Por ejemplo, denotamos la covarianza con un símbolo +:

trait F[+A] // the "+" means "covariant"

Covarianza significa que el tipo F[B] es un subtipo del tipo F[A] si B es un subtipo de A. Esto es útil para modelar muchos tipos, incluidas colecciones como List y Option:

traittraitList[+A]
Option[+A]

La covarianza de las colecciones de Scala nos permite sustituir colecciones de un tipo por una colección de un subtipo en nuestro código. Por ejemplo, podemos usar una Lista[Círculo] en cualquier lugar donde esperemos una Lista[Forma] porque Círculo es un subtipo de Forma:

sealed trait Shape
case class Circle(radius: Double) extends Shape
val circles: List[Circle] = ???
val shapes: List[Shape] = circles

En términos generales, la covarianza se usa para las salidas: datos que luego podemos obtener de un tipo de contenedor como List, o de otra manera devueltos por algún método.

¿Qué pasa con la contravarianza? Escribimos constructores de tipos contravariantes con un símbolo - como este:

trait F[-A]

La contravarianza significa que el tipo F[B] es un subtipo de F[A] si A es un subtipo de B. Esto es útil para modelar tipos que representan entradas, como nuestra clase de tipo JsonWriter anterior:

trait JsonWriter[-A] {
def write(value: A): Json
}

La varianza tiene que ver con la capacidad de sustituir un valor por otro. Considere un escenario en el que tenemos dos valores, uno de tipo Forma y otro de tipo Círculo, y dos JsonWriters, uno para Forma y otro para Círculo:

val shape: Shape = ???
val circle: Circle = ???
val shapeWriter: JsonWriter[Shape] = ???
val circleWriter: JsonWriter[Circle] = ???
def format[A](value: A, writer: JsonWriter[A]): Json = writer.write(value)

¿Qué combinaciones de value y JsonWriter pueden pasar a format? Podemos escribir un Círculo con cualquier escritor porque todos los Círculos son Formas. Por el contrario, no podemos escribir una Forma con circleWriter porque no todas las Formas son Círculos. Esta relación es lo que modelamos formalmente usando contravarianza.

JsonWriter[Shape] es un subtipo de JsonWriter[Circle] porque Circle es un subtipo de Shape. Esto significa que podemos usar shapeWriter en cualquier lugar donde esperemos ver un JsonWriter[Circle].

La invariancia es la situación más fácil de describir. Es lo que obtenemos cuando no escribimos un + o - en un constructor de tipos:

trait F[A]

Esto significa que los tipos F[A] y F[B] nunca son subtipos entre sí, independientemente de la relación entre A y B. Esta es la semántica predeterminada para los constructores de tipos de Scala.

Cuando el compilador busca un implícito, busca uno que coincida con el tipo o subtipo. Por lo tanto, podemos usar anotaciones de varianza para controlar la selección de instancias de clase de tipo hasta cierto punto.

Hay dos problemas que tienden a surgir. Imaginemos que tenemos un tipo de dato algebraico como:

sealed trait A
final case object B extends A
final case object C extends A

Los problemas son:
¿Se seleccionará una instancia definida en un supertipo si hay una disponible? Por ejemplo, ¿podemos definir una instancia para A y hacer que funcione para valores de tipo B y C?
¿Se seleccionará una instancia para un subtipo con preferencia a la de un supertipo? Por ejemplo, si definimos una instancia para A y B, y tenemos un valor de tipo B, ¿se seleccionará la instancia de B con preferencia a A?

Está claro que no existe un sistema perfecto. Cats prefiere usar clases de tipos invariantes.
Esto nos permite especificar instancias más específicas para subtipos si queremos. Significa que si tenemos, por ejemplo, un valor de tipo Some[Int], no se usará nuestra instancia de clase de tipo para Option. Podemos resolver este problema con una anotación de tipo como Some(1) : Option[Int] o usando "constructores inteligentes" como los métodos Option.apply, Option.empty, some y none.

lunes, 7 de marzo de 2022

Empezando con Cats parte 2


Cats proporciona importaciones de sintaxis separadas para cada type class.

Se puede especificar exactamente qué instancia necesitamos. Sin embargo, esto no agrega valor en el código de producción. Es más simple y rápido usar las siguientes importaciones:

• import cats._ importa todas las clases de tipos de Cats de una sola vez;

• import cats.implicits._ importa todas las instancias de clase de tipo estándar y toda la sintaxis de una sola vez.

Podemos definir una instancia de Show simplemente implementando el trait :

import java.util.Date

implicit val dateShow: Show[Date] = new Show[Date] {

def show(date: Date): String = s"${date.getTime}ms since the epoch."

}

new Date().show

// res1: String = "1594650192117ms since the epoch."


Sin embargo, Cats también proporciona un par de métodos convenientes para simplificar el proceso. Hay dos métodos de construcción en el objeto complementario de Show que podemos usar para definir instancias para nuestros propios tipos:

object Show {

// Convert a function to a `Show` instance:

def show[A](f: A => String): Show[A] = ???

// Create a `Show` instance from a `toString` method:

def fromToString[A]: Show[A] = ???

}

Estos nos permiten construir instancias rápidamente con menos ceremonia que definirlas desde cero:

val implícito dateShow: Show[Date] = Show.show(date => s"${date.getTime}ms since the epoch.") 

Como puede ver, el código que usa métodos de construcción es mucho más conciso que el código sin ellos. Muchas clases de tipos en Cats proporcionan métodos auxiliares como estos para construir instancias, ya sea desde cero o transformando instancias existentes para otros tipos.