martes, 10 de mayo de 2022

Currying en Kotlin


Ya hable un monton de currying y no tengo ganas de explicarlo de nuevo, pero ahí va :

Currying es una técnica de transformación de una función con múltiples argumentos de tal manera que se puede llamar como una cadena de funciones cada uno con un único argumento.

Como puedo hacer esto en kotlin? la respuesta es kotlin no soporta currying. Y ya esta :( 

Pero aún podemos crear una función de argumento único que devuelva otra función de argumento único.

Por ejemplo : 

val curriedAdd: (Int) -> (Int) -> Int = { x -> { y -> x + y } }

val add2 : (Int) -> Int = curriedAdd(2)

println(add2(3)) //imprime 5

Como vemos, la primera invocación de curriedAdd devuelve otra función con un parámetro. Lo que hicimos arriba se llama aplicación parcial. Aplicamos solo el primer argumento y preparamos la función para aplicar el segundo.

Eso es básicamente lo que queríamos lograr. Nuestro código puede ser puro, funcional y fácil de probar.

Además, Kotlin no admite curry tal como es. No podemos aplicar solo un argumento a cualquier función. Tenemos que hacer la función con aplicaciones parciales. Pero escribir todas las lambdas como una secuencia de lambdas puede ser molesto.

Y esto es lo que la comunidad hizo por nosotros: una gran biblioteca funcional para Kotlin: funKTionale. El módulo donde viene curry y uncurry es funktionale-currying.

Volviendo a nuestro problema: ¿cómo crear funciones add3, add5, add100 sin generar una gran cantidad de código repetitivo?

Primero, agregue la biblioteca a su script de compilación. En mi caso es gradle:

implementation "org.funktionale:funktionale-currying:1.2"

Y luego podemos hacer : 

val add = { x: Int, y: Int -> x + y}.curried() 

val add3 = add(3)

val add5 = add(5)

val add100 = add(100)

println(add3(3)) // 6

println(add100(-10)) // 90

lunes, 9 de mayo de 2022

Probando Ubuntu Jammy


Hace mucho que no pruebo un linux, la verdad es que estoy muy contento con mi fedora con kde. Pero obligado por la necesidad de salir de la zona de confort he decidido instalar Ubuntu Jammy. 

No me gusto, pero le voy a dar tiempo. Por muchas cosas no me gusto. 

1. La falta de poder personalizarlo, tuve que instalar un software para lograr mejor personalización de la apariencia. 

2. Problemas de instalación que no he tenido en otras distros, tener que por comando agregar un extra para encontrar software. 

3. Falta de organización del software... 

Dejo unas pantallas :





En fin, tal vez no estoy siendo muy parcial. Pero le voy a dar tiempo... 

viernes, 6 de mayo de 2022

Funtores en Cats parte 9

Hay situaciones en las que la eliminación de izquierda a derecha no es la opción correcta. Un ejemplo es el tipo O en Scalactic, que es un equivalente convencionalmente sesgado a la izquierda de O bien:

type PossibleResult = ActualResult Or Error


Otro ejemplo es el funtor contravariante para Function1.
Mientras que el funtor Function1 implementa la composición de función de izquierda a derecha, el funtor Contravariant implementa la composición de derecha a izquierda con estilo de composición. En otras palabras, las siguientes expresiones son todas equivalentes:

val func3a: Int => Double = a => func2(func1(a))
val func3b: Int => Double = func2.compose(func1)

// Hypothetical example. This won't actually compile:
val func3c: Int => Double = func2.contramap(func1)

Sin embargo, si intentamos esto de verdad, nuestro código no se compilará:

import cats.syntax.contravariant._ // for contramap
val func3c = func2.contramap(func1)
// error: value contramap is not a member of Double => Double
// val func3c = func2.contramap(func1)
//
 ^^^^^^^^^^^^^^^

El problema aquí es que la contravariante para la Function1 corrige el tipo de retorno y deja que el tipo de parámetro varíe, lo que requiere que el compilador elimine los parámetros de tipo de derecha a izquierda, como se muestra a continuación

type F[A] = A => Double

El compilador falla simplemente debido a su sesgo de izquierda a derecha. Podemos probar esto creando un alias de tipo que cambia los parámetros en la Función1:

type <=[B, A] = A => B
type F[A] = Double <= A

Si volvemos a escribir func2 como una instancia de <=, restablecemos el orden de eliminación requerido y podemos llamar a contramap como deseemos:

val func2b: Double <= Double = func2
val func3c = func2b.contramap(func1)
// func3c: Int => Double = scala.Function1$$Lambda$7919/0
x00000008424d3040@50061a2d

La diferencia entre func2 y func2b es puramente sintáctica: ambos se refieren al mismo valor y, por lo demás, los alias de tipo son completamente compatibles. Increíblemente, sin embargo, esta simple reformulación es suficiente para darle al compilador la pista que necesita para resolver el problema.
Es raro que tengamos que hacer este tipo de eliminación de derecha a izquierda. La mayoría de los constructores de tipos de parámetros múltiples están diseñados para tener un sesgo hacia la derecha, lo que requiere la eliminación de izquierda a derecha que es compatible con el compilador listo para usar. Sin embargo, es útil conocer esta peculiaridad del orden de eliminación en caso de que alguna vez te encuentres con un escenario extraño como el anterior.


lunes, 2 de mayo de 2022

¿Qué gano aprendiendo Cats?


Aprender Cats no es tan difícil, pero ¿por qué querría hacer eso?

Bueno, porque es realmente útil, solo importando cats.syntax.all._  tiene acceso a un montón de métodos de extensión que resuelven múltiples problemas comunes en una o un par de líneas; por ejemplo:

import cats.syntax.all._


// Combinar 2 mapas

m1 |+| m2


// Sumar el largo de todos los string de una lista

strings.foldMap(str => str.length)


// Aplicar una función, que devuelve una opción, a todos los elementos de una lista

// y devuelve el primero que se define.

ints.foldMapK(foo)


// Aplicar una función, que devuelve una opción, a todos los elementos de una lista

// y volver a recopilar todos los resultados si todos eran Some

ints.traverse(bar)


// Aplicar múltiples validaciones independientes

// y usamos los resultados para crear un type class,

// de lo contrario, acumula todos los errores en una colección no vacía.

(

  validationA(x).toEitherNec,

  validationB(y).toEitherNec,

  validationC(z).toEitherNec

).parMapN(Data.apply)


// Une dos listas de diferentes tamaños:

l1.aligWith(l2) {

  case Ior.both(a, b) => ???

  case Ior.left(a) => ???

  case Ior.right(b) => ???

}

Lo bueno de eso es que esas operaciones son muy generales, por lo que funcionan con múltiples tipos; también suelen componer, por lo que puedes usar las mismas operaciones para tipos más complejos.

Ahora, uno puede argumentar que puede implementarlos usando las funciones stdlib y generalmente puede hacerlo, de la misma manera que puede implementar stdlib usted mismo; el hecho de que esas operaciones ya existan es bueno.

Bueno, también puedes ver a cats como una biblioteca fundamental para construir aún más abstracciones, eso es básicamente lo que hacen bibliotecas como cats-effect y fs2; amplían las abstracciones de cats, además de otras cosas.

Entonces, puede escribir su propia función auxiliar que funcione para cualquier Mónada y estar seguro de que debería funcionar debido a las leyes y todo eso.

Pero, para ser justos, rara vez harás eso, así que no te preocupes demasiado por eso. La mayoría del uso de cats es simplemente importar cats.syntax.all._ y disfrutar de todos los métodos de extensión útiles, o usar estructuras de datos como cats.data.NonEmptychain

En conclusión, cats implementa un framework conceptual para la Programación Funcional, para clasificar combinadores básicos y derivados en una jerarquía de complejidad. 

Dejo link : https://typelevel.org/cats/

domingo, 1 de mayo de 2022

Funtores en Cats parte 8

Vimos una instancia de functor para Function1. 

import cats.Functor

import cats.instances.function._ // for Functor

import cats.syntax.functor._

 // for map

val func1 = (x: Int)  => x.toDouble

val func2 = (y: Double) => y * 2

val func3 = func1.map(func2)

// func3: Int => Double = scala.Function1$$Lambda$7919/0


Function1 tiene dos parámetros de tipo (el argumento de la función y el tipo de resultado):

trait Function1[-A, +B] {
  def apply(arg: A): B
}

Sin embargo, Functor acepta un constructor de tipos con un parámetro:

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(func: A => B): F[B]
}

El compilador tiene que corregir uno de los dos parámetros de Function1 para crear un constructor de tipos del tipo correcto para pasar a Functor. Tiene dos opciones a elegir:

type F[A] = Int => A
type F[A] = A => Double

Sabemos que la primera de ellas es la elección correcta. Sin embargo, el compilador no entiende lo que significa el código. En cambio, se basa en una regla simple, implementando lo que se llama "unificación parcial".

La unificación parcial en el compilador de Scala funciona fijando los parámetros de tipo de izquierda a derecha. En el ejemplo anterior, el compilador corrige Int en Int => Double y busca un Functor para funciones de tipo Int => ?:

type F[A] = Int => A
val functor = Functor[F]

Esta eliminación de izquierda a derecha funciona para una amplia variedad de escenarios comunes, incluidos Functors para tipos como Function1 y Either:

val either: Either[String, Int] = Right(123)
// either: Either[String, Int] = Right(123)
either.map(_ + 1)
// res0: Either[String, Int] = Right(124)


sábado, 30 de abril de 2022

Funtores en Cats parte 7

Entre otros tipos, Cats proporciona una instancia de Invariant para Monoid. 

Imagina que queremos producir un monoide para el tipo símbolo de Scala. Cats no proporciona un Monoide para Símbolo, pero sí proporciona un Monoide para un tipo similar: Cadena o String. Podemos escribir nuestro nuevo semigrupo con un método vacío que se basa en la cadena vacía y un método de combinación que funciona de la siguiente manera:

  1. aceptar dos Símbolos como parámetros;
  2. convertir los Símbolos a Cadenas;
  3. combine las cadenas usando Monoid[String];
  4. convertir el resultado de nuevo en un Símbolo.

Podemos implementar combine usando imap, pasando funciones de tipo String =>Symbol y Symbol => String como parámetros. Aquí está el código, escrito usando el método de extensión imap proporcionado por cats.syntax.invariant:

import cats.Monoid
import cats.instances.string._ // for Monoid
import cats.syntax.invariant._ // for imap
import cats.syntax.semigroup._ // for |+|

implicit val symbolMonoid: Monoid[Symbol] = Monoid[String].imap(Symbol.apply)(_.name) 

Monoid[Symbol].empty
// res3: Symbol = '

Symbol("a") |+| Symbol("few") |+| Symbol("words")
// res4: Symbol = 'afewwords

viernes, 29 de abril de 2022

Consumiendo API con Retrofit


Si has trabajado con microservicios o simplemente has necesitado consumir una API de terceros, notaste que este código no es muy bello y muchas veces se torna complejo.

Retrofit es un framework que viene para salvarnos. 

Veamos un ejemplo, queremos consultar info de los repositorios de github, la información que necesitamos es esta : (voy a trabajar en kotlin porque pinto, es similar en Java) 

import com.google.gson.annotations.SerializedName


data class Repo(

    @SerializedName("id")

    var id: Int? = null,

    @SerializedName("name")

    var name: String,

    @SerializedName("full_name")

    var fullName: String,

    @SerializedName("language")

    var language: String

) {}

Entonces lo que tengo que hacer es agregar estas dependencias a mi proyecto (uso gradle) :

// https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit

implementation("com.squareup.retrofit2:retrofit:2.9.0")

implementation("com.squareup.retrofit2:converter-gson:2.9.0")

y luego tengo que crear una interface que represente el servicio que voy a consumir : 


interface GitHubClient {

    @GET("users/{user}/repos")

    fun listRepos(@Path("user") user: String?): Call<List<Repo?>?>?

}

Y por último debo construir este cliente y usarlo : 


        val retrofit = Retrofit.Builder()

            .baseUrl("https://api.github.com/")

            .addConverterFactory(GsonConverterFactory.create())

            .build()


        val gitHubClient = retrofit.create(GitHubClient::class.java)

        val repos = gitHubClient.listRepos(userName)?.execute()?.body()


Y listo!!    

Dejo link : 

https://square.github.io/retrofit/

lunes, 25 de abril de 2022

Funtores en Cats parte 6

Veamos la implementación de funtores contravariantes e invariantes en Cats, proporcionados por las clases de tipos cats.Contravariant y cats.Invariant.

trait Contravariant[F[_]] {

def contramap[A, B](fa: F[A])(f: B => A): F[B]

}

trait Invariant[F[_]] {

def imap[A, B](fa: F[A])(f: A => B)(g: B => A): F[B]

}

Podemos invocar instancias de Contravariant utilizando el método Contravariant.apply. Cats proporciona instancias para tipos de datos que consumen parámetros, incluidos Eq, Show y Function1. 
Veamos un ejemplo:

import cats.Contravariant
import cats.Show
import cats.instances.string._

val showString = Show[String]
val showSymbol = Contravariant[Show].contramap(showString)((sym: Symbol) => s"'${sym.name}")
showSymbol.show(Symbol("dave"))
// res1: String = "'dave"

Más convenientemente, podemos usar cats.syntax.contravariant, que proporciona un método de extensión de contramap:

import cats.syntax.contravariant._ // for contramap

showString.contramap[Symbol](sym => s"'${sym.name}").show(Symbol("dave"))
// res2: String = "'dave"



viernes, 22 de abril de 2022

Testea tu arquitectura java con ArchUnit


ArchUnit es una biblioteca gratuita, simple y extensible para verificar la arquitectura de su código Java utilizando cualquier framework de testing. Es decir, ArchUnit puede verificar dependencias entre paquetes y clases, capas y segmentos, verificar dependencias cíclicas y más. Lo hace analizando el código de bytes Java, importando todas las clases en una estructura de código Java.

Veamos un ejemplo, queremos poner unas reglas a nuestras interfaces : 


import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;


@AnalyzeClasses(locations = InterfaceRulesTest.RelevantExampleClasses.class)

public class InterfaceRulesTest {


    @ArchTest

    static final ArchRule interfaces_should_not_have_names_ending_with_the_word_interface =

            noClasses().that().areInterfaces().should().haveNameMatching(".*Interface");


    @ArchTest

    static final ArchRule interfaces_should_not_have_simple_class_names_containing_the_word_interface =

            noClasses().that().areInterfaces().should().haveSimpleNameContaining("Interface");


    @ArchTest

    static final ArchRule interfaces_must_not_be_placed_in_implementation_packages =

            noClasses().that().resideInAPackage("..impl..").should().beInterfaces();


    public static class RelevantExampleClasses implements LocationProvider {

        @Override

        public Set<Location> get(Class<?> testClass) {

            Set<Location> result = new HashSet<>();

            result.addAll(Locations.ofClass(SomeBusinessInterface.class));

            result.addAll(Locations.ofClass(SomeDao.class));

            result.addAll(Locations.ofClass(SomeInterfacePlacedInTheWrongPackage.class));

            return result;

        }

    }

}


También podríamos analizar que ningún servicio retorne un objeto del modelo o que estén bien definidas las dependencias o/y los nombres de las clases, etc. 

Veamos otro ejemplo : 

@AnalyzeClasses(packages = "com.tngtech.archunit.example.layers")

public class DaoRulesTest {

    @ArchTest

    static final ArchRule DAOs_must_reside_in_a_dao_package =

            classes().that().haveNameMatching(".*Dao").should().resideInAPackage("..dao..")

                    .as("DAOs should reside in a package '..dao..'");


    @ArchTest

    static final ArchRule entities_must_reside_in_a_domain_package =

            classes().that().areAnnotatedWith(Entity.class).should().resideInAPackage("..domain..")

                    .as("Entities should reside in a package '..domain..'");


    @ArchTest

    static final ArchRule only_DAOs_may_use_the_EntityManager =

            noClasses().that().resideOutsideOfPackage("..dao..")

                    .should().accessClassesThat().areAssignableTo(EntityManager.class)

                    .as("Only DAOs may use the " + EntityManager.class.getSimpleName());



    @ArchTest

    static final ArchRule DAOs_must_not_throw_SQLException =

            noMethods().that().areDeclaredInClassesThat().haveNameMatching(".*Dao")

                    .should().declareThrowableOfType(SQLException.class);

}

Y se pueden hacer muchas cosas más, es muy simple de usar. Agregamos la dependencia : 

dependencies {

    testImplementation 'com.tngtech.archunit:archunit:0.23.1'

}

o con maven : 

<dependency>

    <groupId>com.tngtech.archunit</groupId>

    <artifactId>archunit</artifactId>

    <version>0.23.1</version>

    <scope>test</scope>

</dependency>


Y a codear test!!!

También existe para .net (pero eso es tema para otro post) 

Dejo link : https://www.archunit.org/



miércoles, 20 de abril de 2022

Funtores en Cats parte 5

Podemos pensar en el método map de Functor como "agregar" una transformación a una cadena. Ahora vamos a ver otras dos type class, una que representa anteponer operaciones a una cadena y otra que representa la construcción de una cadena bidireccional de operaciones. Estos se denominan funtores contravariantes e invariantes respectivamente.

La primera de nuestras clases de tipos, el funtor contravariante, proporciona una operación llamada contramapa que representa "anteponer" una operación a una cadena. 

El método contramap solo tiene sentido para tipos de datos que representan transformaciones. Por ejemplo, no podemos definir un contramapa para una opción porque no hay forma de retroalimentar un valor en un Option[B] a través de una función A => B. Sin embargo, podemos definir un contramapa para la clase de tipo Imprimible :

trait Printable[A] {

  def format(value: A): String

}

Un Printable[A] representa una transformación de A a String. Su método contramapa acepta una función func de tipo B => A y crea un nuevo Imprimible[B]:

trait Printable[A] {
  def format(value: A): String
  def contramap[B](func: B => A): Printable[B] = ???
}

def format[A](value: A)(implicit p: Printable[A]): String = p.format(value)

Los funtores invariantes implementan un método llamado imap que es informalmente equivalente a una combinación de map y contramap. Si map genera nuevas instancias de clase de tipo agregando una función a una cadena y contramap las genera anteponiendo una operación a una cadena, imap las genera a través de un par de transformaciones bidireccionales.

Los ejemplos más intuitivos de esto son una clase de tipo que representa la codificación y decodificación como algún tipo de datos, como el formato Play JSON y el códec de scodec. Podemos crear nuestro propio códec mejorando Imprimible para admitir la codificación y decodificación hacia/desde una cadena:

trait Codec[A] {
def encode(value: A): String
def decode(value: String): A
def imap[B](dec: A => B, enc: B => A): Codec[B] = ???
}
def encode[A](value: A)(implicit c: Codec[A]): String =
c.encode(value)
def decode[A](value: String)(implicit c: Codec[A]): A =
c.decode(value)

Si tenemos un Codec[A] y un par de funciones A => B y B => A, el método imap crea un Codec[B]:
Como ejemplo de caso de uso, imagina que tenemos un Codec[String] básico, cuyos métodos de codificación y decodificación simplemente devuelven el valor que se les pasa:

implicit val stringCodec: Codec[String] =
new Codec[String] {
def encode(value: String): String = value
def decode(value: String): String = value
}

Podemos construir muchos códecs útiles para otros tipos construyendo a partir de stringCodec usando imap:

implicit val intCodec: Codec[Int] =
stringCodec.imap(_.toInt, _.toString)
implicit val booleanCodec: Codec[Boolean] =
stringCodec.imap(_.toBoolean, _.toString)

lunes, 18 de abril de 2022

Funtores en Cats parte 4

Podemos definir un funtor simplemente definiendo su método map. Aquí hay un ejemplo de un Functor para Option, aunque tal cosa ya existe en cats.instances. La implementación es trivial: simplemente llamamos al método de mapa de Option:


implicit val optionFunctor: Functor[Option] = new Functor[Option] {

   def map[A, B](value: Option[A])(func: A => B): Option[B] = value.map(func)

}

A veces necesitamos inyectar dependencias en nuestras instancias. Por ejemplo, si tuviéramos que definir un Functor personalizado para Future (otro ejemplo hipotético: Cats proporciona uno en cats.instances.future), tendríamos que tener en cuenta el parámetro ExecutionContext implícito en future.map. No podemos agregar parámetros adicionales a functor.map, por lo que debemos tener en cuenta la dependencia cuando creamos la instancia:

import scala.concurrent.{Future, ExecutionContext}

implicit def futureFunctor(implicit ec: ExecutionContext): Functor[Future] = new Functor[Future] {
  def map[A, B](value: Future[A])(func: A => B): Future[B] = value.map(func)
}

Cada vez que llamamos a un Functor for Future, ya sea directamente usando Functor.apply o indirectamente a través del método de extensión map, el compilador ubicará futureFunctor por resolución implícita y buscará recursivamente un ExecutionContext en el sitio de la llamada. Así es como se vería la expansión:

// We write this:
Functor[Future]

// The compiler expands to this first:
Functor[Future](futureFunctor)

// And then to this:
Functor[Future](futureFunctor(executionContext))

viernes, 15 de abril de 2022

Funtores en Cats parte 3

El método principal proporcionado por la sintaxis de Functor es map. Es difícil demostrar esto con Opciones y Listas, ya que tienen sus propios métodos de mapa integrados y el compilador de Scala siempre preferirá un método integrado a un método de extensión. Resolveremos esto con dos ejemplos. Primero veamos el map sobre funciones. El tipo Function1 de Scala no tiene un método de map (se llama andThen en su lugar), por lo que no hay conflictos de nombres:

import cats.instances.function._ // for Functor

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

val func1 = (a: Int) => a + 1

val func2 = (a: Int) => a * 2

val func3 = (a: Int) => s"${a}!"

val func4 = func1.map(func2).map(func3)

func4(123)

// res3: String = "248!"

Veamos otro ejemplo. Esta vez vamos a abstraernos de los funtores para no trabajar con ningún tipo concreto en particular. Podemos escribir un método que aplique una ecuación a un número sin importar en qué contexto del funtor se encuentre:

def doMath[F[_]](start: F[Int])(implicit functor: Functor[F]): F[Int] = start.map(n => n + 1 * 2)

import cats.instances.option._ // for Functor
import cats.instances.list._  // for Functor

doMath(Option(20))
// res4: Option[Int] = Some(22)
doMath(List(1, 2, 3))
// res5: List[Int] = List(3, 4, 5)

Para ilustrar cómo funciona esto, echemos un vistazo a la definición del método map en cats.syntax.functor. Aquí hay una versión simplificada del código:

implicit class FunctorOps[F[_], A](src: F[A]) {
  def map[B](func: A => B)(implicit functor: Functor[F]): F[B] = functor.map(src)(func)
}

El compilador puede usar este método de extensión para insertar un método map donde no haya un map integrado disponible:

foo.map(value => value + 1)

Suponiendo que foo no tiene un método de map incorporado, el compilador detecta el error potencial y envuelve la expresión en un FunctorOps para corregir el código:

new FunctorOps(foo).map(value => value + 1)

El método map de FunctorOps requiere un Functor implícito como parámetro. Esto significa que este código solo se compilará si tenemos un Funtor para F en el alcance. Si no lo hacemos, obtenemos un error del compilador:

final case class Box[A](value: A)
val box = Box[Int](123)
box.map(value => value + 1)
// error: value map is not a member of repl.Session.App0.Box[Int]
// box.map(value => value + 1)
// ^^^^^^^

El método as también está disponible como sintaxis.

List(1, 2, 3).as("As")
// res7: List[String] = List("As", "As", "As")



jueves, 14 de abril de 2022

EBOOK - Shifting Left for Application Security

Me llego este mail y quiero compartirlo con ustedes:

 

https://interact.f5.com/rs/653-SMC-783/images/EB - Shifting Left for Application Security - 760x284.png

Hi Emanuel,

Security is a paramount concern for developers, operations and security engineers, and company CISOs alike. Security remains crucial from the earliest phases of application design and development through deployment and support. In this report, author Peter Conrad explains why "shifting left" is important and how you can apply this practice in your organization.

In this eBook you will learn:

  • Why you need to adopt DevSecOps and place security at the core of application development and deployment
  • How shifting left embeds security throughout the entire software lifecycle
  • About traditional tools that can help protect clusters, containers, and microservices
  • How a European bank, an energy company, and a telecommunications company successfully shifted left

Funtores en Cats parte 2

La clase de tipo de funtor es cats.Functor. Obtenemos instancias usando el método Functor.apply estándar de un objeto complementario. Como es habitual, las instancias predeterminadas se organizan por tipo en el paquete cats.instances:

import cats.Functor

import cats.instances.list._

 // for Functor

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

val list1 = List(1, 2, 3)

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

val list2 = Functor[List].map(list1)(_ * 2)

// list2: List[Int] = List(2, 4, 6)

val option1 = Option(123)

// option1: Option[Int] = Some(123)

val option2 = Functor[Option].map(option1)(_.toString)

// option2: Option[String] = Some("123")


Functor proporciona un método llamado lift, que convierte una función de tipo A => B en una que opera sobre un funtor y tiene tipo F[A] => F[B]:

val func = (x: Int) => x + 1
// func: Int => Int = <function1>
val liftedFunc = Functor[Option].lift(func)
// liftedFunc: Option[Int] => Option[Int] = cats.Functor$$Lambda$7972
/0x000000084250f840@195657fa
liftedFunc(Option(1))
// res1: Option[Int] = Some(2)

El método as es el otro método que es probable que utilice. Reemplaza con value dentro del Functor con el valor dado.

Functor[List].as(list1, "As")
// res2: List[String] = List("As", "As", "As")

miércoles, 13 de abril de 2022

Libros gratuitos de java code geeks

 

Download IT Guides!

 

Java NIO Programming Cookbook

java.nio (NIO stands for non-blocking I/O) is a collection of Java programming language APIs that offer features for intensive I/O operations. It was introduced with the J2SE 1.4 release...

 
 

IntelliJ IDEA Handbook

IntelliJ IDEA is a Java integrated development environment (IDE) for developing computer software. It is developed by JetBrains, and is available as an Apache 2 Licensed community...

 
 

Amazon S3 Tutorial

Amazon S3 (Simple Storage Service) is a web service offered by Amazon Web Services. Amazon S3 provides storage through web services interfaces (REST, SOAP, and BitTorrent). Amazon...

 
 

JUnit Programming Cookbook

JUnit is a unit testing framework to write repeatable tests. JUnit has been important in the development of test-driven development, and is one of a family of unit testing frameworks...