Translate

miércoles, 7 de diciembre de 2022

Semigroupal, Parallel y Applicative parte 6

Cuando llamamos a product en un tipo que tiene una instancia de Monad, obtenemos una semántica secuencial. Esto tiene sentido desde el punto de vista de mantener la coherencia con las implementaciones de product en términos de flatMap y map. Sin embargo, no siempre es lo que queremos. La clase de tipo Parallel, y su sintaxis asociada, nos permite acceder a semánticas alternativas para ciertas mónadas.

Hemos visto cómo el método del producto en Either se detiene en el primer error.


import cats.Semigroupal

import cats.instances.either._ // for Semigroupal

type ErrorOr[A] = Either[Vector[String], A]

val error1: ErrorOr[Int] = Left(Vector("Error 1"))

val error2: ErrorOr[Int] = Left(Vector("Error 2"))

Semigroupal[ErrorOr].product(error1, error2)

// res0: ErrorOr[(Int, Int)] = Left(Vector("Error 1"))


También podemos escribir esto usando tupled como atajo.


import cats.syntax.apply._ // for tupled

import cats.instances.vector._ // for Semigroup on Vector

(error1, error2).tupled

// res1: ErrorOr[(Int, Int)] = Left(Vector("Error 1"))


Para recopilar todos los errores simplemente reemplazamos tupled con su versión “paralela” llamada parTupled.


import cats.syntax.parallel._ // for parTupled

(error1, error2).parTupled

// res2: ErrorOr[(Int, Int)] = Left(Vector("Error 1", "Error 2"))


¡Se devuelven ambos errores! Este comportamiento no es especial para usar Vector como tipo de error. Cualquier tipo que tenga una instancia de Semigroup funcionará.

Por ejemplo, aquí usamos List en su lugar.


import cats.instances.list._ // for Semigroup on List

type ErrorOrList[A] = Either[List[String], A]

val errStr1: ErrorOrList[Int] = Left(List("error 1"))

val errStr2: ErrorOrList[Int] = Left(List("error 2"))

(errStr1, errStr2).parTupled

// res3: ErrorOrList[(Int, Int)] = Left(List("error 1", "error 2"))


Hay muchos métodos de sintaxis proporcionados por Parallel para métodos en Semigroupal y tipos relacionados, pero el más utilizado es parMapN.

Aquí hay un ejemplo de parMapN en una situación de manejo de errores.


val success1: ErrorOr[Int] = Right(1)

val success2: ErrorOr[Int] = Right(2)

val addTwo = (x: Int, y: Int) => x + y

(error1, error2).parMapN(addTwo)

// res4: ErrorOr[Int] = Left(Vector("Error 1", "Error 2"))

(success1, success2).parMapN(addTwo)

// res5: ErrorOr[Int] = Right(3)


Profundicemos en cómo funciona Parallel. La siguiente definición es el núcleo de Parallel.


trait Parallel[M[_]] {

type F[_]

def applicative: Applicative[F]

def monad: Monad[M]

def parallel: ~>[M, F]

}


Esto nos dice si hay una instancia paralela para algún constructor de tipo M, entonces:

• debe haber una instancia de Monad para M;

• hay un constructor de tipo relacionado F que tiene una instancia Aplicativa; y

• podemos convertir M a F.

No hemos visto ~> antes. Es un alias de tipo para FunctionK y es lo que realiza la conversión de M a F. Una función normal A => B convierte valores de tipo A a valores de tipo B. Recordemos que M y F no son tipos; son constructores de tipos. Una FunciónK M ~> F es una función de un valor con tipo M[A] a un valor con tipo F[A]. Veamos un ejemplo rápido definiendo una FunciónK que convierte un Option en una Lista.

import cats.arrow.FunctionK

object optionToList extends FunctionK[Option, List] {

def apply[A](fa: Option[A]): List[A] =

    fa match {

        case None => List.empty[A]

        case Some(a) => List(a)

    }

}

optionToList(Some(1))

// res6: List[Int] = List(1)

optionToList(None)

// res7: List[Nothing] = List()


Como el parámetro de tipo A es genérico, una función K no puede inspeccionar ningún valor contenido con el constructor de tipo M. La conversión debe realizarse únicamente en términos de la estructura de los constructores de tipo M y F. Podemos en optionToList arriba, este es el caso.

Entonces, en resumen, Parallel nos permite tomar un tipo que tiene una instancia de mónada y convertirlo en algún tipo relacionado que en su lugar tenga una instancia aplicativa (o semigrupal). Este tipo relacionado tendrá algunas semánticas alternativas útiles.

Hemos visto el caso anterior donde el aplicativo relacionado para "O" permite la acumulación de errores en lugar de una semántica rápida.

Ahora que hemos visto Parallel, es hora de aprender finalmente sobre Applicative.

viernes, 2 de diciembre de 2022

Cuando utilizar estructuras, registro o clases en C#?


Que sé yo... Un lenguaje cuantas más cosas trae, a mi entender más complijidad contrae y más dificil es aclarar ciertos puntos... 

Y para colmo se pueden hacer más cosas de diferentes formas, haciendo que se pierda la claridad del mejor camino para resolver las cosas.  Puff ... Y sin duda C#, java, scala, kotlin, etc... son muy completos y por ende muy complejos de entender cuando usar que. 

Luego de esta introducción/opinión, vamos al tema del post. Cuando utilizar estructuras, registro o clases en C#?

¿Puede el tipo de datos ser un tipo de valor? entonces es un estructura. ¿No? ¿Su tipo describe un estado similar a un valor, preferiblemente inmutable? entonces registro. Y sino clase pero podemos usar registro para:  

  • DTO si el flujo unidireccional.
  • Immutable request
  • SearchParameters

Veamos esto con más detalle. Una estructura, una clase y un registro son tipos de datos de usuario.

Las estructuras son tipos de valor. Las clases son tipos de referencia. Los registros son por defecto tipos de referencia inmutables.

Cuando necesita algún tipo de jerarquía para describir sus tipos de datos como herencia o una estructura que apunta a otra estructura o básicamente cosas que apuntan a otras cosas, necesita un tipo de referencia.

Los registros resuelven el problema cuando desea que su tipo esté orientado a valores de forma predeterminada. Los registros son tipos de referencia pero con la semántica orientada al valor.

En conclusión, 

Estructura si: 

  • Representa lógicamente un solo valor, similar a los tipos primitivos (int, double, etc.).
  • Tiene un tamaño de instancia inferior a 16 bytes.
  • Es inmutable
  • No tendrá que ser referenciado con frecuencia.

¿No? Debe ser algún tipo de referencia (clase o registro).

¿El tipo de datos encapsula algún tipo de valor complejo? ¿El valor es inmutable? ¿Lo usa en flujo unidireccional (una vía)? Vamos con registro.

¿No? Vamos con clase.

Por cierto: no te olvides que habrá registros anónimos en C# 10.0, como para ponerle más complejidad.

Otra cosa, una instancia de registro puede ser mutable si la convierte en mutable.


class Program

{

    static void Main()

    {

        var test = new Foo("a");

        Console.WriteLine(test.MutableProperty);

        test.MutableProperty = 15;

        Console.WriteLine(test.MutableProperty);

        //test.Bar = "new string"; // will not compile

    }

}


public record Foo(string Bar)

{

    public double MutableProperty { get; set; } = 10.0;

}


Una asignación de un registro es una copia superficial del registro. Una copia con expresión de un registro no es ni superficial ni profunda. La copia se crea mediante un método de clonación especial emitido por el compilador de C#. Los miembros de tipo de valor se copian y encuadran. Los miembros de tipo de referencia apuntan a la misma referencia. Puede hacer una copia profunda de un registro si y solo si el registro solo tiene propiedades de tipo de valor. Cualquier propiedad de miembro de tipo de referencia de un registro se copia como una copia superficial.

Veamos este ejemplo (usando la función de nivel superior en C# 9.0):


using System.Collections.Generic;

using static System.Console;


var foo = new SomeRecord(new List<string>());

var fooAsShallowCopy = foo;

var fooAsWithCopy = foo with { }; // A syntactic sugar for new SomeRecord(foo.List);

var fooWithDifferentList = foo with { List = new List<string>() { "a", "b" } };

var differentFooWithSameList = new SomeRecord(foo.List); // This is the same like foo with { };

foo.List.Add("a");


WriteLine($"Count in foo: {foo.List.Count}"); // 1

WriteLine($"Count in fooAsShallowCopy: {fooAsShallowCopy.List.Count}"); // 1

WriteLine($"Count in fooWithDifferentList: {fooWithDifferentList.List.Count}"); // 2

WriteLine($"Count in differentFooWithSameList: {differentFooWithSameList.List.Count}"); // 1

WriteLine($"Count in fooAsWithCopy: {fooAsWithCopy.List.Count}"); // 1

WriteLine("");


WriteLine($"Equals (foo & fooAsShallowCopy): {Equals(foo, fooAsShallowCopy)}"); // True. The lists inside are the same.

WriteLine($"Equals (foo & fooWithDifferentList): {Equals(foo, fooWithDifferentList)}"); // False. The lists are different

WriteLine($"Equals (foo & differentFooWithSameList): {Equals(foo, differentFooWithSameList)}"); // True. The list are the same.

WriteLine($"Equals (foo & fooAsWithCopy): {Equals(foo, fooAsWithCopy)}"); // True. The list are the same, see below.

WriteLine($"ReferenceEquals (foo.List & fooAsShallowCopy.List): {ReferenceEquals(foo.List, fooAsShallowCopy.List)}"); // True. The records property points to the same reference.

WriteLine($"ReferenceEquals (foo.List & fooWithDifferentList.List): {ReferenceEquals(foo.List, fooWithDifferentList.List)}"); // False. The list are different instances.

WriteLine($"ReferenceEquals (foo.List & differentFooWithSameList.List): {ReferenceEquals(foo.List, differentFooWithSameList.List)}"); // True. The records property points to the same reference.

WriteLine($"ReferenceEquals (foo.List & fooAsWithCopy.List): {ReferenceEquals(foo.List, fooAsWithCopy.List)}"); // True. The records property points to the same reference.

WriteLine("");


WriteLine($"ReferenceEquals (foo & fooAsShallowCopy): {ReferenceEquals(foo, fooAsShallowCopy)}"); // True. !!! fooAsCopy is pure shallow copy of foo. !!!

WriteLine($"ReferenceEquals (foo & fooWithDifferentList): {ReferenceEquals(foo, fooWithDifferentList)}"); // False. These records are two different reference variables.

WriteLine($"ReferenceEquals (foo & differentFooWithSameList): {ReferenceEquals(foo, differentFooWithSameList)}"); // False. These records are two different reference variables and reference type property hold by these records does not matter in ReferenceEqual.

WriteLine($"ReferenceEquals (foo & fooAsWithCopy): {ReferenceEquals(foo, fooAsWithCopy)}"); // False. The same story as differentFooWithSameList.

WriteLine("");


var bar = new RecordOnlyWithValueNonMutableProperty(0);

var barAsShallowCopy = bar;

var differentBarDifferentProperty = bar with { NonMutableProperty = 1 };

var barAsWithCopy = bar with { };


WriteLine($"Equals (bar & barAsShallowCopy): {Equals(bar, barAsShallowCopy)}"); // True.

WriteLine($"Equals (bar & differentBarDifferentProperty): {Equals(bar, differentBarDifferentProperty)}"); // False. Remember, the value equality is used.

WriteLine($"Equals (bar & barAsWithCopy): {Equals(bar, barAsWithCopy)}"); // True. Remember, the value equality is used.

WriteLine($"ReferenceEquals (bar & barAsShallowCopy): {ReferenceEquals(bar, barAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (bar & differentBarDifferentProperty): {ReferenceEquals(bar, differentBarDifferentProperty)}"); // False. Operator with creates a new reference variable.

WriteLine($"ReferenceEquals (bar & barAsWithCopy): {ReferenceEquals(bar, barAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


var fooBar = new RecordOnlyWithValueMutableProperty();

var fooBarAsShallowCopy = fooBar; // A shallow copy, the reference to bar is assigned to barAsCopy

var fooBarAsWithCopy = fooBar with { }; // A deep copy by coincidence because fooBar has only one value property which is copied into barAsDeepCopy.


WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used.

WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


fooBar.MutableProperty = 2;

fooBarAsShallowCopy.MutableProperty = 3;

fooBarAsWithCopy.MutableProperty = 3;

WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 3

WriteLine($"Equals (fooBar & fooBarAsShallowCopy): {Equals(fooBar, fooBarAsShallowCopy)}"); // True.

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // True. Remember, the value equality is used. 3 != 4

WriteLine($"ReferenceEquals (fooBar & fooBarAsShallowCopy): {ReferenceEquals(fooBar, fooBarAsShallowCopy)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (fooBar & fooBarAsWithCopy): {ReferenceEquals(fooBar, fooBarAsWithCopy)}"); // False. Operator with creates a new reference variable.

WriteLine("");


fooBarAsWithCopy.MutableProperty = 4;

WriteLine($"fooBar.MutableProperty = {fooBar.MutableProperty} | fooBarAsShallowCopy.MutableProperty = {fooBarAsShallowCopy.MutableProperty} | fooBarAsWithCopy.MutableProperty = {fooBarAsWithCopy.MutableProperty}"); // fooBar.MutableProperty = 3 | fooBarAsShallowCopy.MutableProperty = 3 | fooBarAsWithCopy.MutableProperty = 4

WriteLine($"Equals (fooBar & fooBarAsWithCopy): {Equals(fooBar, fooBarAsWithCopy)}"); // False. Remember, the value equality is used. 3 != 4

WriteLine("");


var venom = new MixedRecord(new List<string>(), 0); // Reference/Value property, mutable non-mutable.

var eddieBrock = venom;

var carnage = venom with { };

venom.List.Add("I'm a predator.");

carnage.List.Add("All I ever wanted in this world is a carnage.");

WriteLine($"Count in venom: {venom.List.Count}"); // 2

WriteLine($"Count in eddieBrock: {eddieBrock.List.Count}"); // 2

WriteLine($"Count in carnage: {carnage.List.Count}"); // 2

WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True.

WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // True. Value properties has the same values, the List property points to the same reference.

WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.

WriteLine("");


eddieBrock.MutableList = new List<string>();

eddieBrock.MutableProperty = 3;

WriteLine($"Equals (venom & eddieBrock): {Equals(venom, eddieBrock)}"); // True. Reference or value type does not matter. Still a shallow copy of venom, still true.

WriteLine($"Equals (venom & carnage): {Equals(venom, carnage)}"); // False. the venom.List property does not points to the same reference like in carnage.List anymore.

WriteLine($"ReferenceEquals (venom & eddieBrock): {ReferenceEquals(venom, eddieBrock)}"); // True. The shallow copy.

WriteLine($"ReferenceEquals (venom & carnage): {ReferenceEquals(venom, carnage)}"); // False. Operator with creates a new reference variable.

WriteLine($"ReferenceEquals (venom.List & carnage.List): {ReferenceEquals(venom.List, carnage.List)}"); // True. Non mutable reference type.

WriteLine($"ReferenceEquals (venom.MutableList & carnage.MutableList): {ReferenceEquals(venom.MutableList, carnage.MutableList)}"); // False. This is why Equals(venom, carnage) returns false.

WriteLine("");



public record SomeRecord(List<string> List);


public record RecordOnlyWithValueNonMutableProperty(int NonMutableProperty);


public record RecordOnlyWithValueMutableProperty

{

    public int MutableProperty { get; set; } = 1; // this property gets boxed

}


public record MixedRecord(List<string> List, int NonMutableProperty)

{

    public List<string> MutableList { get; set; } = new();

    public int MutableProperty { get; set; } = 1; // this property gets boxed

}


La penalización de rendimiento es obvia aquí. A mayor cantidad de datos para copiar en una instancia de registro que tenga, mayor penalización de rendimiento obtendrá. En general, debe crear clases pequeñas y livianas y esta regla también se aplica a los registros.

Si su aplicación usa una base de datos o un sistema de archivos, no me preocuparía mucho por esta penalización. Las operaciones de la base de datos/sistema de archivos son generalmente más lentas.


Ebook - Zero Trust Architecture in Kubernetes

 

EBOOK

[O'Reilly eBook] Zero Trust Architecture in Kubernetes

Hi Emanuel,

In this eBook from O’Reilly Media, available for free download courtesy of F5 NGINX, cybersecurity researcher and writer Kim Crawley discusses how the Zero Trust model improves the security posture of your Kubernetes infrastructure and prevents security incidents from damaging your organization.

In this eBook you will learn:

  • The key concepts and principles of the Zero Trust security model
  • How Zero Trust improves the security posture of your organization
  • The technical requirements for Zero Trust in Kubernetes
  • How to apply Zero Trust best practices in your Kubernetes environment

jueves, 1 de diciembre de 2022

Semigroupal, Parallel y Applicative parte 5

La razón de los resultados sorprendentes de List y de Either es que ambas son mónadas. Si tenemos una mónada, podemos implementar el producto de la siguiente manera.


import cats.Monad

import cats.syntax.functor._ // for map

import cats.syntax.flatMap._ // for flatmap

def product[F[_]: Monad, A, B](fa: F[A], fb: F[B]): F[(A,B)] = 

fa.flatMap(a =>

    fb.map(b =>

        (a, b)

    ) 


Sería muy extraño si tuviéramos diferentes semánticas para el producto dependiendo de cómo lo implementemos. Para garantizar una semántica coherente, Cats' Monad (que amplía Semigroupal) proporciona una definición estándar de producto en términos de map y flatmap, como mostramos anteriormente.

Incluso nuestros resultados para Future son un truco de la luz. flatMap proporciona un orden secuencial, por lo que el producto proporciona lo mismo. La ejecución paralela que observamos ocurre porque nuestros futuros constituyentes comienzan a correr antes de que llamemos al producto. Esto es equivalente al patrón clásico create-then-flatMap:


val a = Future("Future 1")

val b = Future("Future 2")

for {

    x <- a

    y <- b

} yield (x, y)


Entonces, ¿por qué molestarse con Semigroupal? La respuesta es que podemos crear tipos de datos útiles que tengan instancias de Semigroupal (y Applicative) pero no Monad. Esto nos libera para implementar el producto de diferentes maneras. 

Semigroupal, Parallel y Applicative parte 4

Semigroupal no siempre proporciona el comportamiento que esperamos, particularmente para los tipos que también tienen instancias de Monad. Hemos visto el comportamiento del Semigroupal para Option. Veamos algunos ejemplos para otros tipos.

La semántica de Future proporciona ejecución paralela en lugar de secuencial:


import cats.Semigroupal

import cats.instances.future._ // for Semigroupal

import scala.concurrent._

import scala.concurrent.duration._

import scala.concurrent.ExecutionContext.Implicits.global

val futurePair = Semigroupal[Future].

product(Future("Hello"), Future(123))

Await.result(futurePair, 1.second)

// res0: (String, Int) = ("Hello", 123)


Los dos futuros comienzan a ejecutarse en el momento en que los creamos, por lo que ya están calculando los resultados en el momento en que llamamos producto. Podemos usar la sintaxis de aplicación para comprimir números fijos de futuros:


import cats.syntax.apply._ // for mapN

case class Cat(

    name: String,

    yearOfBirth: Int,

    favoriteFoods: List[String]

)

val futureCat = (

    Future("Garfield"),

    Future(1978),

    Future(List("Lasagne"))

).mapN(Cat.apply)

Await.result(futureCat, 1.second)

// res1: Cat = Cat("Garfield", 1978, List("Lasagne"))


La combinación de Listas con Semigroupal produce algunos resultados potencialmente inesperados. Podríamos esperar un código como el siguiente para comprimir las listas, pero en realidad obtenemos el producto cartesiano de sus elementos:


import cats.Semigroupal

import cats.instances.list._ // for Semigroupal

Semigroupal[List].product(List(1, 2), List(3, 4))

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


Esto es quizás sorprendente. Comprimir listas tiende a ser una operación más común. 

Podríamos esperar que el producto aplicado a O acumule errores en lugar de fallar rápidamente. Nuevamente, tal vez sorprendentemente, encontramos que el producto implementa el mismo comportamiento de falla rápida que flatMap:

import cats.instances.either._ // for Semigroupal

type ErrorOr[A] = Either[Vector[String], A]

Semigroupal[ErrorOr].product(

Left(Vector("Error 1")),

Left(Vector("Error 2"))

)

// res3: ErrorOr[Tuple2[Nothing, Nothing]] = Left(Vector("Error 1"))

En este ejemplo, el producto ve la primera falla y se detiene, aunque es posible examinar el segundo parámetro y ver que también es una falla.




miércoles, 30 de noviembre de 2022

Semigroupal, Parallel y Applicative parte 3

Cats proporciona una sintaxis de aplicación simplificada. Importamos cats.syntax.apply.


import cats.instances.option._ // for Semigroupal

import cats.syntax.apply._ // for tupled and mapN


El método tuplado se agrega implícitamente a la tupla de Opciones. Utiliza el Semigroupal para Option para comprimir los valores dentro de las Opciones, creando una sola Opción de una tupla:


(Option(123), Option("abc")).tupled

// res8: Option[(Int, String)] = Some((123, "abc"))


Podemos usar el mismo truco en tuplas de hasta 22 valores. Cats define un método tuplado separado para cada aridad:


(Option(123), Option("abc"), Option(true)).tupled

// res9: Option[(Int, String, Boolean)] = Some((123, "abc", true))


Además de tuplado, la sintaxis de aplicación de Cats proporciona un método llamado mapN que acepta un Funtor implícito y una función de la aridad correcta para combinar los valores.


final case class Cat(name: String, born: Int, color: String)

(

Option("Garfield"),

Option(1978),

Option("Orange & black")

).mapN(Cat.apply)

// res10: Option[Cat] = Some(Cat("Garfield", 1978, "Orange & black"))


De todos los métodos mencionados aquí, el más común es usar mapN. Internamente, mapN usa Semigroupal para extraer los valores de Option y Functor para aplicar los valores a la función.

Es bueno ver que esta sintaxis está marcada. Si proporcionamos una función que acepta el número o tipo de parámetros incorrectos, obtenemos un error de compilación:


val add: (Int, Int) => Int = (a, b) => a + b

// add: (Int, Int) => Int = <function2>

(Option(1), Option(2), Option(3)).mapN(add)

// error: ':' expected but '(' found.

// Option("Garfield"),

// ^

(Option("cats"), Option(true)).mapN(add)

/ error: ':' expected but '(' found.

// Option("Garfield"),

// ^


Apply  también tiene métodos contramapN e imapN que aceptan funtores contravariantes e invariantes. Por ejemplo, podemos combinar Monoids usando Invariant. Aquí hay un ejemplo:

import cats.Monoid

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

import cats.instances.invariant._// for Semigroupal

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

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

import cats.syntax.apply._// for imapN


final case class Cat(

   name: String,

   yearOfBirth: Int,

   favoriteFoods: List[String]

)


val tupleToCat: (String, Int, List[String]) => Cat = Cat.apply _

val catToTuple: Cat => (String, Int, List[String]) = cat => (cat.name, cat.yearOfBirth, cat.favoriteFoods)

implicit val catMonoid: Monoid[Cat] = (

    Monoid[String],

    Monoid[Int],

    Monoid[List[String]]

).imapN(tupleToCat)(catToTuple)


Nuestro Monoid nos permite crear Cats "vacíos" y agregar Cats usando la sintaxis :


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

val garfield= Cat("Garfield", 1978, List("Lasagne"))

val heathcliff = Cat("Heathcliff", 1988, List("Junk Food"))

garfield |+| heathcliff

// res14: Cat = Cat("GarfieldHeathcliff", 3966, List("Lasagne", "Junk

Food"))


domingo, 27 de noviembre de 2022

Semigroupal, Parallel y Applicative parte 2

cats.Semigroupal es una clase de tipos que nos permite combinar contextos. Si tenemos dos objetos de tipo F[A] y F[B], un Semigroupal[F] nos permite combinarlos para formar un F[(A, B)]. Su definición en Cats es:


trait Semigroupal[F[_]] {

    def product[A, B](fa: F[A], fb: F[B]): F[(A, B)]

}


Como comentamos en un post anterior, los parámetros fa y fb son independientes entre sí: podemos calcularlos en cualquier orden antes de pasarlos al producto. Esto contrasta con flatMap, que impone un orden estricto en sus parámetros. Esto nos da más libertad cuando definimos instancias de Semigroupal que cuando definimos Monads. 

Mientras que Semigroup nos permite unir valores, Semigroupal nos permite unir contextos. Juntemos algunas Opciones como ejemplo:

import cats.Semigroupal

import cats.instances.option._ // for Semigroupal

Semigroupal[Option].product(Some(123), Some("abc"))

// res1: Option[(Int, String)] = Some((123, "abc"))


Si ambos parámetros son instancias de Some, terminamos con una tupla de los valores dentro. Si alguno de los parámetros se evalúa como None, el resultado completo es None:


Semigroupal[Option].product(None, Some("abc"))

// res2: Option[Tuple2[Nothing, String]] = None

Semigroupal[Option].product(Some(123), None)

// res3: Option[Tuple2[Int, Nothing]] = None


El objeto complementario de Semigroupal define un conjunto de métodos además del producto. Por ejemplo, los métodos tuple2 a tuple22 generalizan el producto a diferentes aridades:


import cats.instances.option._ // for Semigroupal

Semigroupal.tuple3(Option(1), Option(2), Option(3))

// res4: Option[(Int, Int, Int)] = Some((1, 2, 3))

Semigroupal.tuple3(Option(1), Option(2), Option.empty[Int])

// res5: Option[(Int, Int, Int)] = None


Los métodos map2 a map22 aplican una función especificada por el usuario a los valores dentro de 2 a 22 contextos:


Semigroupal.map3(Option(1), Option(2), Option(3))(_ + _ + _)

// res6: Option[Int] = Some(6)

Semigroupal.map2(Option(1), Option.empty[Int])(_ + _)

// res7: Option[Int] = None


También existen métodos contramap2 a contramap22 e imap2 a imap22, que requieren instancias de Contravariant e Invariant respectivamente.

Solo hay una ley para Semigroupal: el método del producto debe ser asociativo.


product(a, product(b, c)) == product(product(a, b), c)



miércoles, 23 de noviembre de 2022

Libros de Java Code Geeks

 

Download FREE IT Guides!

 

JVM Troubleshooting Guide

The Java bytecode produced when application are compiled, is eventually executed by the Java Virtual Machine (JVM). The JVM has grown to be a sophisticated tool, but it essentially...

 
 

Java 8 Features

With no doubts, Java 8 release is the greatest thing in the Java world since Java 5 (released quite a while ago, back in 2004). It brings tons of new features to the Java as a language,...

 
 

JPA Mini Book

One of the problems of Object Orientation is how to map the objects as the database requires. JPA allows us to work with Java classes as it provides a transparent layer to each database...

 
 

Spring Integration for EAI

It is a lightweight framework that builds upon the core Spring framework. It is designed to enable the development of integration solutions typical of event-driven architectures and...

 

Semigroupal, Parallel y Applicative


Los funtores y las mónadas nos permiten secuenciar operaciones usando map y flatMap. Si bien los funtores y las mónadas son abstracciones inmensamente útiles, hay ciertos tipos de flujo de programa que no pueden representar.

Un ejemplo de ello es la validación de formularios. Cuando validamos un formulario, queremos devolver todos los errores al usuario, no detenernos en el primer error que encontremos. Si modelamos esto con una mónada como Either, fallamos rápidamente y perdemos errores. Por ejemplo, el siguiente código falla en la primera llamada a parseInt y no avanza más:


import cats.syntax.either._ // for catchOnly


def parseInt(str: String): Either[String, Int] = Either.catchOnly[NumberFormatException](str.toInt).

leftMap(_ => s"Couldn't read $str")

for {

    a <- parseInt("a")

    b <- parseInt("b")

    c <- parseInt("c")

} yield (a + b + c)

// res0: Either[String, Int] = Left("Couldn't read a")


Otro ejemplo es la evaluación concurrente de Futures. Si tenemos varias tareas independientes de larga duración, tiene sentido ejecutarlas simultáneamente. Sin embargo, la comprensión monádica solo nos permite ejecutarlos en secuencia. map y flatMap no son del todo capaces de capturar lo que queremos porque suponen que cada cálculo depende del anterior:


// context2 is dependent on value1:

context1.flatMap(value1 => context2)


Las llamadas a parseInt y Future.apply anteriores son independientes entre sí, pero map y flatMap no pueden aprovechar esto. Necesitamos una construcción más débil, una que no garantice la secuenciación, para lograr el resultado que queremos. Veremos tres clases de tipos que soportan este patrón:

  • Semigroupal abarca la noción de componer pares de contextos. Cats proporciona un módulo cats.syntax.apply que utiliza Semigroupal y Functor para permitir a los usuarios secuenciar funciones con múltiples argumentos.
  • Parallel convierte tipos con una instancia de Monad en un tipo relacionado con una instancia de Semigroupal.
  • Applicative extiende Semigroupal y Functor. Proporciona una forma de aplicar funciones a parámetros dentro de un contexto.E

Applicatives a menudo se formulan en términos de aplicación de funciones, en lugar de la formulación semigrupal que se enfatiza en Cats. Esta formulación alternativa proporciona un enlace a otras bibliotecas y lenguajes como Scalaz y Haskell. Veremos las diferentes formulaciones de Applicative, así como las relaciones entre Semigroupal, Functor, Applicative y Monad.

sábado, 19 de noviembre de 2022

C# 11 y .NET 7 traen análisis genérico


Uno de los patrones de diseño básicos en .NET es la función Parse estática. Prácticamente todas las clases que se pueden instanciar a partir de una cadena tienen una. Sin embargo, antes de C# 11 y .NET 7, no había forma de crear una interfaz que se adaptara a esto.

El problema fundamental es que antes de ahora, las interfaces abstractas (es decir, la palabra clave de la interfaz) no admitían funciones estáticas. Esto no era solo una limitación de C#, el CLR en sí no lo admitía antes de la introducción de la característica de métodos abstractos estáticos en las interfaces. Al igual que con los métodos de interfaz, los métodos abstractos estáticos pueden tener una implementación predeterminada, pero existen limitaciones.

Veamos interfaz IParsable<TSelf> ofrece dos métodos, Parse y TryParse. Ambos aceptan un String y un IFormatProvider. Para acceder a ellos, necesita un método de ayuda genérico como:


static T Parse<T>(string s, IFormatProvider? provider)

    where T : IParsable<T>

{

    return T.Parse(s, provider);

}


Se puede invocarlo usando un parámetro de tipo.


var a = Parse<int>("4", null);



martes, 15 de noviembre de 2022

Roslyn, compiladores de C# y Visual Basic con unas APIs para crear herramientas de análisis de código.

Roslyn es la implementación de código abierto de los compiladores de C# y Visual Basic con unas APIs para crear herramientas de análisis de código.

La idea es que el compilador exponga información del proceso de compilación para crear herramientas que permitan mejorar ese código. 

Los compiladores procesan el código siguiendo reglas estructuradas que a menudo difieren de la forma en que los humanos leen y entienden el código. Una comprensión básica del modelo utilizado por los compiladores es esencial para comprender las API que utiliza al crear herramientas basadas en Roslyn.

El SDK de .NET Compiler Platform expone el análisis de código de los compiladores de C# y Visual Basic, al proporcionar una capa de API que refleja una canalización de compilador tradicional.

Cada fase de este proceso es un componente separado. Primero, la fase de análisis tokeniza y analiza el texto fuente en sintaxis que sigue la gramática del lenguaje. En segundo lugar, la fase de declaración analiza la fuente y los metadatos importados para formar símbolos con nombre. A continuación, la fase de enlace hace coincidir los identificadores en el código con los símbolos. Finalmente, la fase de emisión emite un ensamblado con toda la información construida por el compilador.

En correspondencia con cada una de esas fases, el SDK de .NET Compiler Platform expone un modelo de objeto que permite el acceso a la información en esa fase. La fase de análisis expone un árbol de sintaxis, la fase de declaración expone una tabla de símbolos jerárquicos, la fase de vinculación expone el resultado del análisis semántico del compilador y la fase de emisión es una API que produce códigos de bytes IL.


Cada compilador combina estos componentes como un único todo de extremo a extremo.

Estas API son las mismas que usa Visual Studio. Por ejemplo, las funciones de esquematización y formato del código usan los árboles de sintaxis, el Examinador de objetos y las funciones de navegación usan la tabla de símbolos, las refactorizaciones e Ir a definición usan el modelo semántico, y Edit and Continue usa todo esto, incluida la API Emit.

La capa del compilador contiene los modelos de objetos que corresponden a la información expuesta en cada fase de la canalización del compilador, tanto sintáctica como semántica. La capa del compilador también contiene una instantánea inmutable de una única invocación de un compilador, incluidas las referencias de ensamblado, las opciones del compilador y los archivos de código fuente. Hay dos API distintas que representan el lenguaje C# y el lenguaje Visual Basic. Las dos API tienen una forma similar pero están diseñadas para ofrecer alta fidelidad a cada lenguaje individual. Esta capa no tiene dependencias en los componentes de Visual Studio.

Como parte de su análisis, el compilador puede producir un conjunto de diagnósticos que cubren todo, desde errores de sintaxis, semánticos y de asignación definitiva hasta varias advertencias y diagnósticos informativos. La capa de API del compilador expone diagnósticos a través de una API extensible que permite que los analizadores se conecten al proceso de compilación. Permite que los diagnósticos definidos por el usuario, como los producidos por herramientas como StyleCop, se produzcan junto con los diagnósticos definidos por el compilador. Producir diagnósticos de esta manera tiene la ventaja de integrarse naturalmente con herramientas como MSBuild y Visual Studio, que dependen de los diagnósticos para experiencias como detener una compilación basada en una política y mostrar IL en vivo en el editor y sugerir correcciones de código.

Las API de alojamiento y secuencias de comandos forman parte de la capa del compilador. Puede usarlos para ejecutar fragmentos de código y acumular un contexto de ejecución en tiempo de ejecución. El REPL interactivo de C# (Read-Evaluate-Print Loop) utiliza estas API. El REPL le permite utilizar C# como lenguaje de secuencias de comandos, ejecutando el código de forma interactiva a medida que lo escribe.

La capa Workspaces contiene la API Workspace, que es el punto de partida para realizar análisis de código y refactorizar soluciones completas. Lo ayuda a organizar toda la información sobre los proyectos en una solución en un solo modelo de objeto, ofreciéndole acceso directo a los modelos de objetos de la capa del compilador sin necesidad de analizar archivos, configurar opciones o administrar dependencias de proyecto a proyecto.

Además, la capa Workspaces muestra un conjunto de API que se utilizan al implementar herramientas de análisis y refactorización de código que funcionan dentro de un entorno de host como el IDE de Visual Studio. Los ejemplos incluyen las API Buscar todas las referencias, Formato y Generación de código. Esta capa no tiene dependencias en los componentes de Visual Studio.

viernes, 11 de noviembre de 2022

Microsoft lanza .NET 7



Y porque es tan importante esto?  .NET 7 es una plataforma de desarrollo multiplataforma unificada. 

La idea era unificar .NET Core (reescrito desde cero, open source y multiplataforma) con la tecnología de .NET Framework (más versátil, pero limitada a sistemas Windows), así como con productos como Xamarin y Mono, para crear un producto que permitiera programar sobre una única base de código con runtimes y experiencias de desarrollo uniformes.

Sin embargo, en agosto de 2020, Microsoft anunció que estos pasos se pospondrían hasta el lanzamiento de .NET 6, previsto para noviembre de 2021. Tras eso, la compañía anunció "cambios en el cronograma" que obligaron a postergar de nuevo la integración de componentes como MAUI (Multi-platform App UI). Y así hasta hoy.

Una de las grandes novedades de .NET 7 es la inclusión de la versión 11 de la sintaxis de C#, el lenguaje de programación estrella de Microsoft (F#, también ha sido actualizado).

Además, el anuncio de Microsoft incluye referencias a mejoras de rendimiento, especialmente en ARM64 (con un rendimiento hasta un 45% superior) y acuerdos de colaboración para desarrollar con .NET en Ubuntu Linux, y en los servidores Power System de IBM.

Dejo link : https://news.microsoft.com/es-xl/net-7-esta-disponible-hoy/

miércoles, 9 de noviembre de 2022

Free eBook: Declarative Cloud Infrastructure Management with Terraform (New Edition)

 

Download Now

 

Free eBook: Declarative Cloud Infrastructure Management with Terraform (New Edition)


Understand the underlying concepts of Terraform, declarative cloud infrastructure management, and key basics of the Hashicorp Configuration Language.

Also covered: why and when you should use Linode Kubernetes Engine (LKE) and Terraform together, Packer explained, and links to more educational content.

Get the PDF instantly (2.6 MB) — no registration required
Download Now
Your Suggestions
Any ideas or suggestions? Shoot us an email at newsletter@javacodegeeks.com

Scalafmt


Cuando trabajas con mucha gente tenes que definir que formato de código van utilizar. Es decir, no puede cada uno dar formato de código como le venga en ganas porque cuando tengan que interactuar por medio de git van a tener que subir más código y más que mergear. 

Por ende existen formateadores de código que te permiten formatear el código de forma estandar y listo!! Unos de esos formateadores es Scalafmt que es para scala. Por lo tanto podemos dedicar más tiempo a discutir cuestiones importantes en la revisión del código y menos tiempo al estilo del código. Scalafmt formatea el código para que se vea consistente entre las personas.

Se puede ejecutar scalafmt desde el editor, herramienta de compilación o terminal. Scalafmt tiene integraciones con IntelliJ, sbt, Maven, Gradle y Mill.

Dejo link: 

https://scalameta.org/scalafmt/

sábado, 5 de noviembre de 2022

Un ejemplo de uso de Go routines


Un ejemplo sencillo de Go routines, tenemos 2 Slices con diferentes números y queremos que se impriman en go routines diferentes y bueno, tengo que poner un time.Sleep(time.Second) para esperar que termine. 

Debería haber usado un sync.WaitGroup pero no lo hice, igual hay un ejemplo acá.


package main

import (

"fmt"

"time"

"math/rand"

)


func main(){

s1, s2 := []int{1,2,3}, []int{4,5,6}

fmt.Println("Let's Race !!!!!!!!!!!!\n")

go printElemOfIntSlice(s1, 100)

go printElemOfIntSlice(s2, 100)

time.Sleep(time.Second)

}


func printElemOfIntSlice(slice []int, velo int){

for _, val  := range slice{

fmt.Println(val)

duration := rand.Intn(velo)

time.Sleep(time.Duration(duration) * time.Millisecond)

}

}