Translate

sábado, 13 de agosto de 2022

Interfaces en Kotlin


Las interfaces de Kotlin son similares a las de Java 8: pueden contener definiciones de métodos abstractos, así como implementaciones de métodos no abstractos (similares a los métodos predeterminados de Java 8), pero no pueden contener ningún estado. Para declarar una interfaz en Kotlin, se usa la palabra clave de interface en lugar de class.


interface Clickable {

    fun click()

}


Esto declara una interfaz con un solo método abstracto llamado clic. Todas las clases no abstractas que implementan la interfaz deben proporcionar una implementación de este método. 

class Button : Clickable { override fun click() = println("I was clicked") } 

>>> Button().click() 

I was clicked


Kotlin usa los dos puntos después del nombre de la clase para reemplazar las palabras clave extends e implements que se usan en Java. Como en Java, una clase puede implementar tantas interfaces como quiera, pero solo puede extender una clase.

El modificador override, similar a la anotación @Override en Java, se utiliza para marcar métodos y propiedades que anulan los de la superclase o la interfaz. A diferencia de Java, el uso del modificador de anulación es obligatorio en Kotlin. Esto le evita anular accidentalmente un método si se agrega después de escribir su implementación; su código no se compilará a menos que marque explícitamente el método como anulado o le cambie el nombre.

Un método de interfaz puede tener una implementación predeterminada. A diferencia de Java 8, que requiere que marques tales implementaciones con la palabra clave predeterminada, Kotlin no tiene una anotación especial para tales métodos: solo proporcionas un cuerpo de método. Cambiemos la interfaz Clickable agregando un método con una implementación predeterminada


interface Clickable { 

  fun click() 

  fun showOff() = println("I'm clickable!") 

}


Si implementa esta interfaz, debe proporcionar una implementación para hacer clic. Puede redefinir el comportamiento del método showOff o puede omitirlo si está de acuerdo con el comportamiento predeterminado. Supongamos ahora que otra interfaz también define un método showOff y tiene la siguiente implementación para él.


interface Focusable {

    fun setFocus(b: Boolean) = println("I ${if (b) "got" else "lost"} focus.")

    fun showOff() = println("I'm focusable!")

}


¿Qué sucede si necesita implementar ambas interfaces en su clase? Cada uno de ellos contiene un método showOff con una implementación predeterminada; ¿Qué implementación gana? Ninguno de los dos gana. En su lugar, obtiene el siguiente error del compilador si no implementa showOff explícitamente


The class 'Button' must override public open fun showOff() because it inherits many implementations of it


El compilador de Kotlin te obliga a proporcionar tu propia implementación.


class Button : Clickable, Focusable { 

    override fun click() = println("I was clicked")

    override fun showOff() { 

        super<Clickable>.showOff()

        super<Focusable>.showOff() } 

}


La clase Button ahora implementa dos interfaces. Implementa showOff() llamando a ambas implementaciones que heredó de los supertipos. Para invocar una implementación heredada, usa la misma palabra clave que en Java: super. Pero la sintaxis para seleccionar una implementación específica es diferente. Mientras que en Java puede colocar el nombre del tipo base antes de la palabra clave super, como en Clickable.super.showOff(), en Kotlin coloca el nombre del tipo base entre paréntesis angulares: super<Clickable>.showOff().

Si solo necesita invocar una implementación heredada, puede escribir esto:


override fun showOff() = super.showOff()


Puede crear una instancia de esta clase y verificar que se pueden llamar todos los métodos heredados


fun main(args: Array) { 

    val button = Button() 

    button.showOff() 

    button.setFocus(true) 

    button.click() 

}


La implementación de setFocus se declara en la interfaz Focusable y se hereda automáticamente en la clase Button.   

viernes, 12 de agosto de 2022

Creando un web service REST con ZIO


 Vamos hacer un pequeño proyecto con ZIO, como para empezar. El "hola mundo" de toda la vida pero en un servicio REST. 

Antes de empezar vamos a hacer un proyecto con scala 3 con sbt : 


sbt new scala/scala3.g8


Luego agregamos las dependencias de zio, el build.sbt debe quedar así : 


scalaVersion := "3.1.2"

organization := "dev.zio"

name         := "zio-quickstart-restful-webservice"


libraryDependencies ++= Seq(

  "dev.zio"       %% "zio"            % "2.0.0",

  "dev.zio"       %% "zio-json"       % "0.3.0-RC10",

  "io.d11"        %% "zhttp"        % "2.0.0-RC10",

  "io.getquill"   %% "quill-zio"    % "4.2.0",

  "io.getquill"   %% "quill-jdbc-zio" % "4.2.0",

  "com.h2database" % "h2"             % "2.1.214"

)


Aclaro que tengo otras dependencias como la de base de dato que las voy a usar en proximos ejemplos. 

Y ahore si, hacemos el web service para nuestro "hola mundo" : 


package dev.zio.quickstart.greet


import zhttp.http._


object GreetingApp {

  def apply(): Http[Any, Nothing, Request, Response] =

    Http.collect[Request] {

      // GET /greet?name=:name

      case req@(Method.GET -> !! / "greet") if (req.url.queryParams.nonEmpty) =>

        Response.text(s"Hello ${req.url.queryParams("name").mkString(" and ")}!")


      // GET /greet

      case Method.GET -> !! / "greet" =>

        Response.text(s"Hello World!")


      // GET /greet/:name

      case Method.GET -> !! / "greet" / name =>

        Response.text(s"Hello $name!")

    }

}



Luego programamos el Main :


package dev.zio.quickstart


import dev.zio.quickstart.counter.CounterApp

import dev.zio.quickstart.download.DownloadApp

import dev.zio.quickstart.greet.GreetingApp

import dev.zio.quickstart.users.{InmemoryUserRepo, PersistentUserRepo, UserApp}

import zhttp.service.Server

import zio._


object MainApp extends ZIOAppDefault {

  def run =

    Server.start(

      port = 8999,

      http = GreetingApp() 

    ).provide(

      // An layer responsible for storing the state of the `counterApp`

      ZLayer.fromZIO(Ref.make(0)),

      

      // To use the persistence layer, provide the `PersistentUserRepo.layer` layer instead

      InmemoryUserRepo.layer

    )

}


Y listo!! Si vamos a http://localhost:8999/greet?name=Emanuel me va a saludar. 

Para correrlo lo pueden hacer con sbt run o con intellij corriendo el Main. 


Dejo el repo : 

https://github.com/emanuelpeg/zio-quickstart-restful-webservice

lunes, 8 de agosto de 2022

Primeros pasos con ZIO


Vamos hacer un pequeño proyecto con ZIO, como para empezar. El "hola mundo" de toda la vida. 

Antes de empezar vamos a hacer un proyecto con scala 3 con sbt : 

sbt new scala/scala3.g8

Luego agregamos las dependencias de zio, en este caso solo usaremos : 


libraryDependencies += "dev.zio" %% "zio" % "2.0.0"


De esta manera el archivo build.sbt será : 


val scala3Version = "3.1.3"


lazy val root = project

  .in(file("."))

  .settings(

    name := "zioHello",

    version := "0.1.0-SNAPSHOT",


    scalaVersion := scala3Version,

    libraryDependencies += "dev.zio" %% "zio" % "2.0.0",

    libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test

  )


Y luego vamos a hacer nuestro "hola mundo" en el archivo Main.scala : 


import zio._
import zio.Console._

object Main extends ZIOAppDefault :

  def run = myAppLogic

  val myAppLogic =
    for {
      _    <- printLine("Hola, como te llamas guapo?")
      nombre <- readLine
      _    <- printLine(s"Hola, ${nombre}, welcome to ZIO!")
    } yield ()


Y listo!! 


domingo, 7 de agosto de 2022

Programación Funcional con Scala y ZIO 2.0

Quiero compartirles un vídeo de scalac que esta muy bueno y nos da una primera mirada a ZIO :


viernes, 5 de agosto de 2022

Get me to the Cluster: Providing External Access to Services in Kubernetes with NGINX and BGP

 

WHITEPAPER

[Whitepaper] Get me to the Cluster: Providing External Access to Services in Kubernetes with NGINX and BGP

Hi Emanuel,

Learn how to use NGINX and BGP to provide external access to services running in Kubernetes. This whitepaper discusses the detailed solution architecture and components and shares step-by-step implementation instructions to help you deploy the solution.

In this whitepaper you will learn:

  • About the real-world challenges of exposing containerized applications in Kubernetes externally
  • How NGINX and BGP can help provide external access to services running in Kubernetes
  • About the detailed solution architecture and components
  • How to implement the solution with complete, step-by-step instructions

DevOps como puntapié inicial para tu transformación digital

 Me llego este mail y me resulto interesante, por eso se los comparto : 

miércoles, 3 de agosto de 2022

Mónadas en Cats parte 21

cats.data.State nos permite pasar estados adicionales como parte de un cálculo. Definimos instancias de estado que representan operaciones de estado atómico y las unimos usando map y flatMap. De esta manera, podemos modelar el estado mutable de una manera puramente funcional, sin usar la mutación real.

Reducido a su forma más simple, las instancias de State[S, A] representan funciones de tipo S => (S, A). S es el tipo del estado y A es el tipo del resultado.


import cats.data.State

val a = State[Int, String]{ state =>

(state, s"The state is $state")

}


En otras palabras, una instancia de State es una función que hace dos cosas:

• transforma un estado de entrada en un estado de salida;

• calcular un resultado.

Podemos "ejecutar" nuestra mónada desde un estado inicial. State proporciona tres métodos (run, runS y runA) que devuelven diferentes combinaciones de estado y resultado. Cada método devuelve una instancia de Eval, que State usa para mantener la seguridad de la pila. Llamamos al método de valor como de costumbre para extraer el resultado real:


// Get the state and the result:

val (state, result) = a.run(10).value

// state: Int = 10

// result: String = "The state is 10"

// Get the state, ignore the result:

val justTheState = a.runS(10).value

// justTheState: Int = 10

// Get the result, ignore the state:

val justTheResult = a.runA(10).value

// justTheResult: String = "The state is 10"

Mónadas en Cats parte 20

Los Readers proporcionan una herramienta para realizar inyección de dependencia. Escribimos pasos de nuestro programa como instancias de Reader, los encadenamos junto con map y flatMap, y construimos una función que acepta la dependencia como entrada.

Hay muchas formas de implementar la inyección de dependencia en Scala, desde técnicas simples como métodos con múltiples listas de parámetros, pasando por parámetros implícitos y clases de tipos, hasta técnicas complejas como el patrón cake  y frameworks.

Los Readers son más útiles en situaciones donde:

• estamos construyendo un programa que puede ser fácilmente representado por una función;

• necesitamos diferir la inyección de un parámetro conocido o conjunto de parámetros;

• queremos poder probar partes del programa de forma aislada.

Al representar los pasos de nuestro programa como Lectores, podemos probarlos tan fácilmente como funciones puras, además de obtener acceso a los combinadores map y flatMap.

Para problemas más complicados donde tenemos muchas dependencias, o donde un programa no se representa fácilmente como una función pura, otras técnicas de inyección de dependencia tienden a ser más apropiadas.


lunes, 1 de agosto de 2022

Mónadas en Cats parte 19

cats.data.Reader es una mónada que nos permite secuenciar operaciones que dependen de alguna entrada. Las instancias de Reader envuelven funciones de un argumento, brindándonos métodos útiles para componerlas.

Un uso común para Readers es la inyección de dependencia. Si tenemos varias operaciones que dependen de alguna configuración externa, podemos encadenarlas usando un Reader para producir una gran operación que acepte la configuración como un parámetro y ejecute nuestro programa en el orden especificado.

Podemos crear un Reader[A, B] a partir de una función A => B usando el constructor Reader.apply:


import cats.data.Reader

final case class Cat(name: String, favoriteFood: String)

val catName: Reader[Cat, String] = Reader(cat => cat.name)

// catName: Reader[Cat, String] = Kleisli(<function1>)


Podemos extraer la función nuevamente usando el método de ejecución del Reader's y llamarla usando apply:


catName.run(Cat("Garfield", "lasagne"))

// res1: cats.package.Id[String] = "Garfield"


El poder de Readers proviene de sus métodos map y flatMap, que representan diferentes tipos de composición de funciones. Por lo general, creamos un conjunto de Readers que aceptan el mismo tipo de configuración, los combinamos con map y flatMap, y luego llamamos a ejecutar para inyectar la configuración al final.

El método map simplemente extiende el cálculo en el Reader al pasar su resultado a través de una función:


val greetKitty: Reader[Cat, String] = catName.map(name => s"Hello ${name}")

greetKitty.run(Cat("Heathcliff", "junk food"))

// res2: cats.package.Id[String] = "Hello Heathcliff"


El método flatMap es más interesante. Nos permite combinar Readers que dependen del mismo tipo de entrada. Para ilustrar esto, ampliemos nuestro ejemplo de saludo para alimentar también al gato:


val feedKitty: Reader[Cat, String] = Reader(cat => s"Have a nice bowl of ${cat.favoriteFood}")

val greetAndFeed: Reader[Cat, String] =

for {

greet <- greetKitty

feed <- feedKitty

} yield s"$greet. $feed."

greetAndFeed(Cat("Garfield", "lasagne"))

// res3: cats.package.Id[String] = "Hello Garfield. Have a nice bowl of lasagne."

greetAndFeed(Cat("Heathcliff", "junk food"))

// res4: cats.package.Id[String] = "Hello Heathcliff. Have a nice bowl of junk food."



Log con Aop de Spring boot


Supongamos que queremos logear cada cosa que pasa por cada método de nuestros objetos, para hacer esto podemos utilizar aop. 

Veamos un ejemplo: 

@Aspect

@Configuration

@Order(0)

class LogingAspect {

    private val logger = LogFactory.getLog(LoggingAspect::class.java)


    @Pointcut("execution(public * com.paquete..*(..))")

    fun allControllersMethods() {

    }


    @Around("allControllersMethods()")

    fun profileAllMethods(pjp: ProceedingJoinPoint): Any {

        val method: Method = (pjp.signature as MethodSignature).method


        logger.info("Started ${method.name}")


        try {

            return if (pjp.args != null) {

                pjp.proceed(pjp.args)

            } else {

                pjp.proceed()

            }

        } catch (ex: Exception) {

            logger.error("Error in ${method.name}", ex)

            throw ex

        } finally {

            logger.info( "Finished ${method.name}")

        }

    }

}


Y Listo!!

viernes, 29 de julio de 2022

Crear un proyecto con Sbt y Scala 3


Es muy fácil, mi consejo, instalate sdkman y luego sbt, pero si queres instalarte sbt de una, no hay problema. 

Luego lo que tienes que hacer es : 

> sbt new scala/scala3.g8

Y al toque te pide nombre del proyecto, lo llenas y listo!!

Espero que les sirva. 

jueves, 28 de julio de 2022

Compilar en kotlin


El código fuente de Kotlin normalmente se almacena en archivos con la extensión .kt. El compilador de Kotlin analiza el código fuente y genera archivos .class, al igual que lo hace el compilador de Java. Los archivos .class generados luego se empaquetan y ejecutan utilizando el procedimiento estándar para el tipo de aplicación en la que está trabajando. En el caso más simple, puede usar el comando kotlinc para compilar su código desde la línea de comandos y usar el comando java para ejecutar su código:


kotlinc <source file or directory> -include-runtime -d <jar name>

java -jar <jar name>


El código compilado con el compilador de Kotlin depende de la biblioteca de runtime de Kotlin. Contiene las definiciones de las propias clases de biblioteca estándar de Kotlin y las extensiones que Kotlin agrega a las API estándar de Java. La biblioteca de runtime debe distribuirse con su aplicación.

En la mayoría de los casos de la vida real, usará un sistema de compilación como Maven, Gradle o Ant para compilar su código. Kotlin es compatible con todos esos sistemas de compilación. Todos esos sistemas de compilación también admiten proyectos de lenguaje mixto que combinan Kotlin y Java en la misma base de código. Además, Maven y Gradle se encargan de incluir la biblioteca en tiempo de ejecución de Kotlin como una dependencia de tu aplicación.

miércoles, 27 de julio de 2022

Filosofía de Kotlin


Cuando hablamos de Kotlin, es un lenguaje pragmático, conciso y seguro con un enfoque en la interoperabilidad. ¿Qué significan exactamente con cada una de esas palabras? 

Pragmático: Kotlin es un lenguaje práctico diseñado para resolver problemas del mundo real. Su diseño se basa en muchos años de experiencia en la industria creando sistemas a gran escala, y sus funciones se eligen para abordar los casos de uso que encuentran muchos desarrolladores de software. Además, los desarrolladores tanto dentro de JetBrains como en la comunidad han estado usando las primeras versiones de Kotlin durante varios años y sus comentarios han dado forma a la versión lanzada del lenguaje. Kotlin puede ayudar a resolver problemas en proyectos reales.

Kotlin tampoco es un lenguaje de investigación. No estamos tratando de avanzar en el estado del arte en el diseño de lenguajes de programación y explorar ideas innovadoras en informática. En su lugar, siempre que sea posible, confiamos en funciones y soluciones que ya han aparecido en otros lenguajes de programación y han demostrado ser exitosas. Esto reduce la complejidad del lenguaje y hace que sea más fácil de aprender al permitirle confiar en conceptos familiares.

 Además, Kotlin no impone el uso de ningún estilo o paradigma de programación en particular. A medida que comienza a estudiar el lenguaje, puede usar el estilo y las técnicas que le resultan familiares de su experiencia con Java. Más adelante, descubrirá gradualmente las funciones más potentes de Kotlin y aprenderá a aplicarlas en su propio código, para hacerlo más conciso e idiomático.

Otro aspecto del pragmatismo de Kotlin es su enfoque en las herramientas. Un entorno de desarrollo inteligente es tan esencial para la productividad de un desarrollador como un buen lenguaje; y debido a eso, tratar el soporte IDE como una ocurrencia tardía no es una opción. En el caso de Kotlin, el complemento IntelliJ IDEA se desarrolló al unísono con el compilador, y las características del lenguaje siempre se diseñaron teniendo en cuenta las herramientas.

La compatibilidad con IDE también juega un papel importante a la hora de ayudarte a descubrir las características de Kotlin. En muchos casos, las herramientas detectarán automáticamente patrones de código comunes que se pueden reemplazar por construcciones más concisas y se ofrecerán a corregir el código por usted. Al estudiar las características del lenguaje utilizadas por las correcciones automáticas, también puede aprender a aplicar esas características en su propio código.

Conciso: Es bien sabido que los desarrolladores dedican más tiempo a leer código existente que a escribir código nuevo. Imagina que eres parte de un equipo que está desarrollando un gran proyecto y necesitas agregar una nueva función o corregir un error. ¿Cuáles son tus primeros pasos? Busca la sección exacta del código que necesita cambiar y solo entonces implementa una corrección. Lees mucho código para saber lo que tienes que hacer. Este código puede haber sido escrito recientemente por sus colegas, por alguien que ya no trabaja en el proyecto, o por usted, pero hace mucho tiempo. Solo después de comprender el código que lo rodea, puede realizar las modificaciones necesarias.

Cuanto más simple y conciso sea el código, más rápido entenderá lo que está pasando. Por supuesto, el buen diseño y los nombres expresivos juegan un papel importante aquí. Pero la elección del lenguaje y su concisión también son importantes. El lenguaje es conciso si su sintaxis expresa claramente la intención del código que lee y no lo oscurece con el texto estándar requerido para especificar cómo se logra la intención.

 En Kotlin, todo el código que escribe tiene un significado y no esta ahí solo para satisfacer los requisitos de la estructura del código. Gran parte del código estándar de Java, como getters, setters y la lógica para asignar parámetros de constructor a los campos, está implícito en Kotlin y no abarrota el código fuente.

Otra razón por la que el código puede ser innecesariamente largo es tener que escribir código explícito para realizar tareas comunes, como ubicar un elemento en una colección. Al igual que muchos otros lenguajes modernos, Kotlin tiene una rica biblioteca estándar que le permite reemplazar estas secciones largas y repetitivas de código con llamadas a métodos de biblioteca. La compatibilidad de Kotlin con lambdas facilita el paso de pequeños bloques de código a funciones de biblioteca. Esto le permite encapsular todas las partes comunes en la biblioteca y mantener solo la porción única y específica de la tarea en el código de usuario.

Al mismo tiempo, Kotlin no intenta reducir el código fuente al menor número de caracteres posible. Por ejemplo, aunque Kotlin admite la sobrecarga de operadores, los usuarios no pueden definir sus propios operadores. Por lo tanto, los desarrolladores de bibliotecas no pueden reemplazar los nombres de los métodos con secuencias de puntuación crípticas. Las palabras suelen ser más fáciles de leer que la puntuación y es más fácil encontrar documentación.

Un código más conciso requiere menos tiempo para escribir y, lo que es más importante, menos tiempo para leer. Esto mejora la productividad y permite hacer las cosas más rápido.

Seguro: En general, cuando hablamos de un lenguaje de programación como seguro, nos referimos a que su diseño previene ciertos tipos de errores en un programa. Por supuesto, esto no es un absoluto igualdad; ningún lenguaje previene todos los posibles errores. Además, la prevención de errores generalmente tiene un costo. Debe proporcionar al compilador más información sobre la operación prevista del programa, de modo que el compilador pueda verificar que la información coincida con lo que hace el programa. Por eso, siempre hay una compensación entre el nivel de seguridad que se obtiene y la pérdida de productividad necesaria para realizar anotaciones más detalladas.

Con Kotlin, se logra un mayor nivel de seguridad que en Java, con un costo total menor. La ejecución en la JVM ya proporciona muchas garantías de seguridad: por ejemplo, seguridad de la memoria, prevención de desbordamientos de búfer y otros problemas causados ​​por el uso incorrecto de la memoria asignada dinámicamente. Como lenguaje tipificado estáticamente en JVM, Kotlin también garantiza la seguridad de tipos de sus aplicaciones. Esto tiene un costo menor que con Java: no tiene que especificar todas las declaraciones de tipos, porque en muchos casos el compilador infiere los tipos automáticamente.

Kotlin también va más allá, lo que significa que se pueden evitar más errores mediante comprobaciones en tiempo de compilación en lugar de fallar en tiempo de ejecución. Lo más importante es que Kotlin se esfuerza por eliminar NullPointerException de su programa. El sistema de tipos de Kotlin realiza un seguimiento de los valores que pueden y no pueden ser nulos y prohíbe las operaciones que pueden conducir a una excepción NullPointer en tiempo de ejecución. El costo adicional requerido para esto es mínimo: marcar un tipo como anulable requiere solo un carácter, un signo de interrogación al final:

val s: String? = null

val s2: String = ""

Además, Kotlin proporciona muchas formas convenientes de manejar datos anulables. Esto es de gran ayuda para eliminar los bloqueos de aplicaciones.

Otro tipo de excepción que Kotlin ayuda a evitar es ClassCastException. Ocurre cuando conviertes un objeto en un tipo sin verificar primero que tenga el tipo correcto. En Java, los desarrolladores a menudo omiten la verificación, porque el nombre del tipo debe repetirse en la verificación y en el siguiente lanzamiento. En Kotlin, por otro lado, la verificación y el elenco se combinan en una sola operación: una vez que hayas verificado el tipo, puedes referirte a los miembros de ese tipo sin ningún casteo adicional. Por lo tanto, no hay motivo para omitir la verificación y no hay posibilidad de cometer un error. Así es como funciona esto:

if (value is String) println(value.toUpperCase())

Interoperable: Con respecto a la interoperabilidad, su primera preocupación probablemente sea: "¿Puedo usar mis bibliotecas existentes?" Con Kotlin, la respuesta es: "Sí, absolutamente". Independientemente del tipo de API que la biblioteca requiera que uses, puedes trabajar con ellas desde Kotlin. Puede llamar a métodos de Java, extender clases de Java e implementar interfaces, aplicar anotaciones de Java a sus clases de Kotlin, etc.

 A diferencia de otros lenguajes JVM, Kotlin va aún más allá con la interoperabilidad, lo que hace que llamar al código Kotlin desde Java también sea sencillo. No se requieren trucos: las clases y los métodos de Kotlin se pueden llamar exactamente como las clases y los métodos de Java normales. Esto le brinda la máxima flexibilidad para combinar código Java y Kotlin en cualquier parte de su proyecto. Cuando comience a adoptar Kotlin en su proyecto de Java, puede ejecutar el convertidor de Java a Kotlin en cualquier clase de su base de código, y el resto del código seguirá compilando y funcionando sin modificaciones. Esto funciona independientemente del rol de la clase que haya convertido.

Otra área en la que Kotlin se centra en la interoperabilidad es el uso de las bibliotecas Java existentes en la mayor medida posible. Por ejemplo, Kotlin no tiene su propia biblioteca de colecciones. Se basa completamente en las clases de la biblioteca estándar de Java y las amplía con funciones adicionales para un uso más conveniente en Kotlin. (Veremos el mecanismo para esto con más detalle en la sección 3.3). Esto significa que nunca necesitará envolver o convertir objetos cuando llame a las API de Java desde Kotlin, o viceversa. Toda la riqueza de API proporcionada por Kotlin viene sin costo en tiempo de ejecución.

 Las herramientas de Kotlin también brindan soporte completo para proyectos en varios lenguajes. Puede compilar una combinación arbitraria de archivos fuente de Java y Kotlin, independientemente de cómo dependan entre sí. Las funciones del IDE también funcionan en todos los lenguajes, lo que le permite:

  • Navegar libremente entre los archivos fuente de Java y Kotlin
  • Depurar proyectos de lenguaje mixto y pasar entre código escrito en diferentes lenguajes
  • Refactorizar sus métodos Java y actualice correctamente su uso en el código Kotlin, y viceversa. 



lunes, 25 de julio de 2022

Interceptor de Retrofit

                                                                                                                                                                                                                                                                        

Supongamos que tenemos que enviar en todas las llamadas a web services, un valor o un token o cualquier cosa. 

Lo que podemos hacer con retrofit es un interceptor, este lo declaramos una vez y para todas las llamadas enviarán los valores. Veamos un ejemplo: 

@Component

class RetrofitClient {


    @Value("\${service.url}")

    private lateinit var baseUrl: String


    private lateinit var retrofit: Retrofit


    private lateinit var objectMapper: ObjectMapper


    @PostConstruct

    fun init() {

        objectMapper = ObjectMapper()

            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

            .registerModule(KotlinModule())


        val client = OkHttpClient.Builder().addInterceptor(EjemploInterceptor()).build()


        retrofit = Retrofit.Builder()

            .baseUrl(baseUrl)

            .client(client)

            .addConverterFactory(JacksonConverterFactory.create(objectMapper))

            .build()

    }


    fun <T> buildService(service: Class<T>): T {

        return retrofit.create(service)

    }


}


class EjemploInterceptor : Interceptor {


    companion object {

        private const val EJEMPLO = "ejemplo"

    }


    override fun intercept(chain: Interceptor.Chain): Response = chain.run {

        proceed(

            request()

                .newBuilder()

                .addHeader(EJEMPLO, "valor a agregar")

                .build()

        )

    }

}


En este ejemplo en todas las llamadas enviarán el valor agregado en el head. Y listo! 

jueves, 21 de julio de 2022

Mónadas en Cats parte 18

El log en un Writer se conserva cuando hacemos map or flatMap sobre él. flatMap agrega los logs del Writer de origen y el resultado de la función de secuenciación del usuario. Por esta razón, es una buena práctica utilizar un tipo de registro que tenga operaciones de adición y concatenación eficientes, como un Vector:

val writer1 = for {

    a <- 10.pure[Logged]

    _ <- Vector("a", "b", "c").tell

    b <- 32.writer(Vector("x", "y", "z"))

} yield a + b

// writer1: cats.data.WriterT[cats.package.Id, Vector[String], Int] =WriterT(

//(Vector("a", "b", "c", "x", "y", "z"), 42)

// )

writer1.run

// res3: (Vector[String], Int) = (Vector("a", "b", "c", "x", "y", "z") , 42)


Además de transformar el resultado con map y flatMap, podemos transformar el log en un Writer con el método mapWritten:


val writer2 = writer1.mapWritten(_.map(_.toUpperCase))

// writer2: cats.data.WriterT[cats.package.Id, Vector[String], Int] = WriterT(

// (Vector("A", "B", "C", "X", "Y", "Z"), 42)

// )

writer2.run

// res4: (Vector[String], Int) = (Vector("A", "B", "C", "X", "Y", "Z") , 42)


Podemos transformar tanto el log como el resultado simultáneamente usando bimap o mapBoth. bimap toma dos parámetros de función, uno para el registro y otro para el resultado. mapBoth toma una sola función que acepta dos parámetros:


val writer3 = writer1.bimap(

    log => log.map(_.toUpperCase),

    res => res * 100

)

// writer3: cats.data.WriterT[cats.package.Id, Vector[String], Int] = WriterT(

//(Vector("A", "B", "C", "X", "Y", "Z"), 4200)

// )

writer3.run

// res5: (Vector[String], Int) = (Vector("A", "B", "C", "X", "Y", "Z"), 4200)

val writer4 = writer1.mapBoth { (log, res) =>

    val log2 = log.map(_ + "!")

    val res2 = res * 1000

    (log2, res2)

}

// writer4: cats.data.WriterT[cats.package.Id, Vector[String], Int] =WriterT(

//(Vector("a!", "b!", "c!", "x!", "y!", "z!"), 42000)

// )

writer4.run

// res6: (Vector[String], Int) = (

//Vector("a!", "b!", "c!", "x!", "y!", "z!"),

//42000

// )


Finalmente, podemos borrar el registro con el método de reinicio e intercambiar el registro y el resultado con el método de intercambio:


val writer5 = writer1.reset

// writer5: cats.data.WriterT[cats.package.Id, Vector[String], Int] = WriterT(

//(Vector(), 42)

// )

writer5.run

// res7: (Vector[String], Int) = (Vector(), 42)

val writer6 = writer1.swap

// writer6: cats.data.WriterT[cats.package.Id, Int, Vector[String]] = WriterT(

//(42, Vector("a", "b", "c", "x", "y", "z"))

// )

writer6.run

// res8: (Int, Vector[String]) = (42, Vector("a", "b", "c", "x", "y","z"))