Translate

Mostrando las entradas con la etiqueta Monoides. Mostrar todas las entradas
Mostrando las entradas con la etiqueta Monoides. Mostrar todas las entradas

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.

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.