jueves, 29 de junio de 2023

Slices en Rust


Un segmento o slices te da una vista de una colección más grande:

 fn main() {

    let a: [i32; 6] = [10, 20, 30, 40, 50, 60];

    println!("a: {a:?}");


    let s: &[i32] = &a[2..4];

    println!("s: {s:?}");

}

$ cargo run

   Compiling hello_cargo v0.1.0 

    Finished dev [unoptimized + debuginfo] target(s) in 1.62s

     Running `target/debug/hello_cargo`

a: [10, 20, 30, 40, 50, 60]

s: [30, 40]


Creamos un segmento a partir de un arreglo y especificando los índices inicial y final entre paréntesis.

Si el segmento comienza en el índice 0, la sintaxis de rango de Rust nos permite descartar el índice inicial, lo que significa que &a[0..a.len()] y &a[..a.len()] son idénticos.

Lo mismo ocurre con el último índice, por lo que &a[2..a.len()] y &a[2..] son idénticos.

Para crear fácilmente una porción de la matriz completa, podemos usar &a[..].

s es una referencia a una porción de i32s. Y el tipo de s es &[i32] y no menciona la longitud del vector. Esto nos permite realizar cálculos en rebanadas de diferentes tamaños.

Las rebanadas siempre toman prestado de otro objeto. En este ejemplo, a tiene que permanecer "vivo" durante al menos el mismo tiempo que nuestro segmento.

La pregunta sobre la modificación de a[3] puede generar una discusión interesante, pero la respuesta es que, por razones de seguridad de la memoria, no puede hacerlo a través de a después de crear un segmento, pero puede leer los datos de a y s de forma segura. 


miércoles, 28 de junio de 2023

Rust y Referencias colgantes


 Rust prohibirá las referencias colgantes:


fn main() {

    let ref_x: &i32;

    {

        let x: i32 = 10;

        ref_x = &x;

    }

    println!("ref_x: {ref_x}");

}


Se dice que una referencia "toma prestado" el valor al que se refiere.

Rust está rastreando la vida útil de todas las referencias para garantizar que vivan lo suficiente. Y lanza el siguiente error: 


$ cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

error[E0597]: `x` does not live long enough

  --> src/main.rs:9:17

   |

9  |         ref_x = &x;

   |                 ^^ borrowed value does not live long enough

10 |

11 |     }

   |     - `x` dropped here while still borrowed

12 |

13 |     println!("ref_x: {ref_x}");

   |                       ----- borrow later used here


For more information about this error, try `rustc --explain E0597`.

error: could not compile `hello_cargo` due to previous error


lunes, 26 de junio de 2023

Rust: Tipos de compuestos

 

TypesLiterals
Arrays[T; N][20, 30, 40][0; 3]
Tuples()(T,)(T1, T2), …()('x',)('x', 1.2), …


Asignación de matriz y acceso:

fn main() {
    let mut a: [i8; 10] = [42; 10];
    a[5] = 0;
    println!("a: {:?}", a);
}

Asignación de tuplas y acceso:

fn main() {
    let t: (i8, bool) = (7, true);
    println!("1st index: {}", t.0);
    println!("2nd index: {}", t.1);
}

Arrays:

Los Arrays tienen elementos del mismo tipo, T, y longitud, N, que es una constante de tiempo de compilación. Tenga en cuenta que la longitud de la matriz es parte de su tipo, lo que significa que [u8; 3] y [u8; 4] se consideran dos tipos diferentes.

Podemos usar literales para asignar valores a matrices.


Tuplas:

Al igual que los arreglos, las tuplas tienen una longitud fija.

Las tuplas agrupan valores de diferentes tipos en un tipo compuesto.

Se puede acceder a los campos de una tupla por el período y el índice del valor, p. t.0, t.1.

La tupla vacía () también se conoce como el "tipo de unidad". Es a la vez un tipo y el único valor válido de ese tipo, es decir, tanto el tipo como su valor se expresan como (). Se utiliza para indicar, por ejemplo, que una función o expresión no tiene valor de retorno, como veremos más adelante.

Puede considerarlo como un void que puede resultarle familiar de otros lenguajes de programación.

Referencias en Rust


Como C++, Rust tiene referencias:

fn main() {

    let mut x: i32 = 10;

    let ref_x: &mut i32 = &mut x;

    *ref_x = 20;

    println!("x: {x}");

}

Debemos desreferenciar ref_x al asignarleun valor, de forma similar a los punteros de C y C++.

Rust eliminará automáticamente la referencia en algunos casos, en particular al invocar métodos.

Las referencias que se declaran como mut se pueden vincular a diferentes valores durante su vida útil.

Asegúrese de notar la diferencia entre let mut ref_x: &i32 y let ref_x: &mut i32. El primero representa una referencia mutable que se puede vincular a diferentes valores, mientras que el segundo representa una referencia a un valor mutable.

viernes, 23 de junio de 2023

Rust: Comandos básicos y tipos


Gran parte de la sintaxis de Rust le resultará familiar de C, C++ o Java:

  • Los bloques y ámbitos están delimitados por llaves.
  • Los comentarios de línea comienzan con //, los comentarios de bloque están delimitados por /* ... */.
  • Palabras clave como if y while funcionan de la misma manera.
  • La asignación de variables se realiza con =, la comparación se realiza con ==.

Los tipos escalares son : 


TypesLiterals
Signed integersi8i16i32i64i128isize-1001_000123i64
Unsigned integersu8u16u32u64u128usize012310u16
Floating point numbersf32f643.14-10.0e202f32
Strings&str"foo""two\nlines"
Unicode scalar valueschar'a''α''∞'
Booleansbooltruefalse

Cuanto bites usan: 

iN, uN y fN tienen N bits,
isize y usize son el ancho de un puntero,
char tiene 32 bits,
bool tiene un ancho de 8 bits.

Las cadenas sin formato permite crear un valor &str con escapes deshabilitados: r"\n" == "\\n". Puede incrustar comillas dobles usando una cantidad igual de # a cada lado de las comillas:

fn main() {
    println!(r#"<a href="link.html">link</a>"#);
    println!("<a href=\"link.html\">link</a>");
}
Las cadenas de bytes le permiten crear un valor &[u8] directamente:

fn main() {
    println!("{:?}", b"abc");
    println!("{:?}", &[97, 98, 99]);
}

sábado, 17 de junio de 2023

Primeros pasos con Rust


Quiero recomendarles el sitio primeros pasos en Rust, para aprender de 0 el lenguaje Rust. La pagina dice: 

¿Está interesado en aprender un nuevo lenguaje de programación que está creciendo en uso y popularidad? ¡Empiece por aquí! Siente las bases del conocimiento que necesita para compilar programas rápidos y eficaces en Rust.

En esta ruta de aprendizaje, hará lo siguiente:

  • Instalar las herramientas necesarias para escribir sus primeras líneas de código de Rust.
  • Aprender los conceptos básicos de Rust.
  • Aprender a administrar los errores.
  • Administrar la memoria en Rust.
  • Usar tipos y rasgos genéricos.
  • Configurar módulos para paquetes y contenedores.
  • Escribir y ejecutar pruebas automatizadas.
  • Crear una herramienta de línea de comandos.

Sin más dejo link:  https://learn.microsoft.com/es-es/training/paths/rust-first-steps/

jueves, 15 de junio de 2023

Ya tenemos los resultados de la encuesta de stackoverflow

 


Como dije, ya tenemos los resultados de la encuesta de stackoverflow!!!


Dejo link: 

https://survey.stackoverflow.co/2023/

¿Por qué Rust?



Los puntos más fuertes de Rust son:

  • Seguridad de la memoria.
  • Comportamiento bien definido en tiempo de ejecución.
  • Características del lenguaje moderno.

Si lo comparamos con otros lenguajes :

Experiencia con C o C++: Rust elimina toda una clase de errores de tiempo de ejecución. Obtiene un rendimiento como en C y C++, pero no tiene los problemas de inseguridad de la memoria. Además, es un lenguaje moderno con construcciones como pattern matching y la gestión de dependencias integrada.

Experiencia con Java, Go, Python, JavaScript…: Obtiene la misma seguridad de memoria que en esos lenguajes, además de una sensación de lenguaje de alto nivel similar. Además, obtiene un rendimiento rápido y predecible como C y C++ (sin recolector de basura), así como acceso a hardware de bajo nivel (si lo necesita).

Supera 6 desafíos clave de la nube con Google Cloud.

 

Un pequeño ejemplo en Rust


Un pequeño programa de ejemplo en Rust:

fn main() {              // Program entry point

    let mut x: i32 = 6;  // Mutable variable binding

    print!("{x}");       // Macro for printing, like printf

    while x != 1 {       // No parenthesis around expression

        if x % 2 == 0 {  // Math like in other languages

            x = x / 2;

        } else {

            x = 3 * x + 1;

        }

        print!(" -> {x}");

    }

    println!();

}


Si corremos este programa: 

$ cargo run

   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)

    Finished dev [unoptimized + debuginfo] target(s) in 0.48s

     Running `target/debug/hello_cargo`

6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1


El código implementa la conjetura de Collatz: se cree que el ciclo siempre terminará, pero esto aún no está probado. 

Que podemos ver de este código: 

  • Todas las variables están tipadas estáticamente. Podemos eliminar i32 para activar la inferencia de tipo :  let mut x = 6; 
  • mut se utiliza para indicar que x es mutable. 
  • print! se utiliza para imprimir. 


miércoles, 14 de junio de 2023

¿Qué es Rust?


Rust es un nuevo lenguaje de programación que tuvo su versión 1.0 en 2015:

  • Rust es un lenguaje compilado estáticamente con un papel similar al de C++
    • rustc usa LLVM como backend.
  • Rust admite muchas plataformas y arquitecturas:
    • x86, ARM, WebAssembly, …
    • Linux, Mac, Windows,…
  • Rust se utiliza para una amplia gama de dispositivos:
    • firmware y cargadores de arranque,
    • pantallas inteligentes,
    • teléfonos móviles,
    • escritorios,
    • servidores.

Rust encaja en la misma área que C++:
  • Alta flexibilidad.
  • Alto nivel de control.
  • Se puede programar para dispositivos muy limitados, como teléfonos móviles.
  • No tiene runtime, ni recolección de basura.
  • Se centra en la fiabilidad y la seguridad sin sacrificar el rendimiento.
Pasemos al programa Rust más simple posible, un programa clásico de Hello World:

fn main() {
    println!("Hello 🌍!");
}

$ cargo run
   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.26s
     Running `target/debug/hello_cargo`
Hello 🌍!

Lo que ves:
  • Las funciones se introducen con fn.
  • Los bloques están delimitados por llaves como en C y C++.
  • La función principal es el punto de entrada del programa.
  • Rust tiene macros higiénicas, println! es un ejemplo de esto.
  • Las cadenas Rust están codificadas en UTF-8 y pueden contener cualquier carácter Unicode.

Puntos clave:
  • Rust es muy parecido a otros lenguajes en la tradición C/C++/Java. Es imperativo (no funcional) y no intenta reinventar las cosas a menos que sea absolutamente necesario.
  • Rust es moderno con soporte completo para cosas como Unicode.
  • Rust usa macros para situaciones en las que desea tener una cantidad variable de argumentos (sin sobrecarga de funciones).
  • Las macros que son "higiénicas" significa que no capturan accidentalmente identificadores del ámbito en el que se utilizan. Las macros de Rust son en realidad solo parcialmente higiénicas.

lunes, 12 de junio de 2023

Compilar y ejecutar código Rust con Cargo


Si tenemos instalados bien rust y cargo esto debería funcionar: 

$ rustc --version

rustc 1.65.0

$ cargo --version

cargo 1.65.0


Veamos como crear un binario de Rust a partir de un ejemplo:


$ cargo new exercise

     Created binary (application) `exercise` package

$ cd exercise
$ cargo run
   Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)
    Finished dev [unoptimized + debuginfo] target(s) in 0.75s
     Running `target/debug/exercise`
Hello, world!


Reemplace el código de src/main.rs con su propio código. Por ejemplo:

fn main() {
    println!("Hola Rust!");
}

Utilizamos cargo run : 

$ cargo run
   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 1.06s
     Running `target/debug/hello_cargo`
Hola Rust!

Usamos cargo check para revisar rápidamente el proyecto en busca de errores, usa cargo build para compilarlo sin ejecutarlo. Se encontrará el resultado en target/debug/. Usamos cargo build --release para producir una compilación de versión optimizada en target/release/.

Se puede agregar dependencias para su proyecto editando Cargo.toml. Cuando ejecutamos los comandos de cargo, automáticamente descargará y compilará las dependencias faltantes para usted.

$ cargo build --release
   Compiling hello_cargo v0.1.0 (/home/emanuel/Projects/rust/hello_cargo)
    Finished release [optimized] target(s) in 0.26s

$ cd target/release/
$ ./exercise
Hola Rust!

Y listo!!






jueves, 8 de junio de 2023

Primeros pasos con ZIO parte 4


ZIO puede convertir cualquier código (como una llamada a algún método) en un efecto, ya sea que ese código se llame síncrono (que devuelve directamente un valor) o asíncrono (que pasa un valor a las devoluciones de llamada).

Si se hace correctamente, cuando convierte el código en un efecto ZIO, este código se almacenará dentro del efecto para que ZIO pueda administrarlo y beneficiarse de funciones como reintentos, tiempos de espera y registro automático de errores.

Las funciones de conversión que tiene ZIO le permiten utilizar sin problemas todas las funciones de ZIO con código que no es de ZIO escrito en Scala o Java, incluidas las bibliotecas de terceros.

El código síncrono se puede convertir en un efecto ZIO usando ZIO.attempt:


import scala.io.StdIn

val readLine: ZIO[Any, Throwable, String] = ZIO.attempt(StdIn.readLine())


El tipo de error del efecto resultante siempre será Throwable, porque el código síncrono puede generar excepciones con cualquier valor de tipo Throwable.

Si sabe con certeza que algún código no arroja excepciones (excepto quizás excepciones en tiempo de ejecución), puede convertir el código en un efecto ZIO usando ZIO.succeed:


def printLine(line: String): UIO[Unit] =  ZIO.succeed(println(line))


A veces, es posible que sepa que el código arroja un tipo de excepción específico, y es posible que desee reflejar esto en el parámetro de error de su efecto ZIO.

Para ello, puede utilizar el método ZIO#refineToOrDie:


import java.io.IOException


val readLine2: ZIO[Any, IOException, String] =

  ZIO.attempt(StdIn.readLine()).refineToOrDie[IOException]


El código asíncrono que expone una API basada en devolución de llamada se puede convertir en un efecto ZIO usando ZIO.async:

object legacy {

  def login(

    onSuccess: User => Unit,

    onFailure: AuthError => Unit): Unit = ???

}


val login: ZIO[Any, AuthError, User] =

  ZIO.async[Any, AuthError, User] { callback =>

    legacy.login(

      user => callback(ZIO.succeed(user)),

      err  => callback(ZIO.fail(err))

    )

  }


Los efectos asincrónicos son mucho más fáciles de usar que las API basadas en devolución de llamadas y se benefician de las características de ZIO como la interrupción, la seguridad de los recursos y la gestión de errores.

Algunos códigos sincrónicos pueden participar en el llamado bloqueo de E/S, que pone un subproceso en un estado de espera, mientras espera que se complete alguna llamada del sistema operativo. Para obtener el máximo rendimiento, este código no debe ejecutarse en el grupo de subprocesos principal de su aplicación, sino en un grupo de subprocesos especial dedicado a operaciones de bloqueo.

ZIO tiene un grupo de subprocesos de bloqueo integrado en el tiempo de ejecución y le permite ejecutar efectos allí con ZIO.blocking:


import scala.io.{ Codec, Source }


def download(url: String) =

  ZIO.attempt {

    Source.fromURL(url)(Codec.UTF8).mkString

  }


def safeDownload(url: String) =

  ZIO.blocking(download(url))


Como alternativa, si desea convertir el código de bloqueo directamente en un efecto ZIO, puede usar el método ZIO.attemptBlocking:


val sleeping =

  ZIO.attemptBlocking(Thread.sleep(Long.MaxValue))


El efecto resultante se ejecutará en el grupo de subprocesos de bloqueo de ZIO.

Si tiene algún código síncrono que responderá a Thread.interrupt de Java (como Thread.sleep o código basado en bloqueo), entonces puede convertir este código en un efecto ZIO interrumpible usando el método ZIO.attemptBlockingInterrupt.

Algunos códigos síncronos solo se pueden cancelar invocando algún otro código, que es responsable de cancelar el cálculo en ejecución. Para convertir dicho código en un efecto ZIO, puede usar el método ZIO.attemptBlockingCanceble:


import java.net.ServerSocket

import zio.UIO


def accept(l: ServerSocket) =

  ZIO.attemptBlockingCancelable(l.accept())(ZIO.succeed(l.close()))


lunes, 5 de junio de 2023

Pekko: El fork open source de Akka que impulsa la escalabilidad en sistemas concurrentes


En el mundo de la programación concurrente y distribuida, Akka ha sido una herramienta ampliamente reconocida y utilizada. Sin embargo, recientemente hubo un cambio de licencia que no cayo nada bien pero por suerte aparecio Pekko, un fork open source de Akka que busca llevar la escalabilidad de sistemas concurrentes a un nuevo nivel. 

¿Qué es Pekko? Pekko es un fork open source de Akka, un framework de programación concurrente y distribuida basado en el modelo de actores. Al igual que Akka, Pekko proporciona una forma elegante y eficiente de construir sistemas altamente concurrentes y escalables. 

Arquitectura de bajo acoplamiento: Pekko ha sido diseñado con una arquitectura de bajo acoplamiento que permite un mejor aislamiento y desacoplamiento entre los actores. Esto se traduce en una mayor flexibilidad y capacidad de escalar los sistemas de forma eficiente.

Rendimiento mejorado: Pekko ha optimizado el rendimiento en comparación con Akka, especialmente en escenarios de alto tráfico y carga intensiva. Gracias a su implementación eficiente y algoritmos optimizados, Pekko es capaz de manejar un mayor número de mensajes por segundo y reducir la latencia en las comunicaciones.

Escalabilidad elástica: Pekko ha introducido un enfoque innovador para la escalabilidad elástica, permitiendo que los sistemas se adapten automáticamente a las demandas cambiantes de carga. Esto se logra mediante la capacidad de ajustar dinámicamente la cantidad de recursos asignados a los actores en función de la demanda, lo que garantiza un rendimiento óptimo en todo momento.

Mejoras en la tolerancia a fallos: Pekko ha fortalecido los mecanismos de tolerancia a fallos, lo que garantiza una mayor robustez en entornos distribuidos. Mediante la implementación de estrategias de recuperación y supervisión mejoradas, Pekko ayuda a gestionar los errores y a mantener la estabilidad del sistema, incluso en situaciones adversas.

Facilidad de migración: Pekko ha sido diseñado para facilitar la migración desde Akka. Los desarrolladores pueden aprovechar su experiencia previa con Akka y adaptarse rápidamente a Pekko sin mayores obstáculos.

Comunidad activa: Pekko cuenta con una comunidad open source vibrante y activa. Esto implica que los desarrolladores pueden colaborar, compartir conocimientos y contribuir a la mejora continua del proyecto.

Documentación exhaustiva: Pekko ofrece una documentación completa y bien estructurada, que incluye guías, tutoriales y ejemplos de código. Esto facilita la adopción y el aprendizaje de la herramienta.

Por ahora Pekko esta en incubador de apache pero creo que tiene futuro. 

Pekko representa un emocionante avance en el mundo de la programación concurrente y distribuida. Con su enfoque en la escalabilidad, rendimiento y facilidad de uso, este fork open source de Akka ofrece una alternativa sólida y poderosa para desarrolladores y arquitectos de sistemas. Si estás interesado en construir sistemas concurrentes altamente escalables, Pekko definitivamente merece tu atención.

Dejo link : https://pekko.apache.org/

Mi experiencia con la programación funcional en Scala


Creo que si leen este blog saben que me encanta la programación funcional y investigar y aprender. Y en eso estoy...


Me gustaría contar un poco mi evolución y mis altibajos para que la gente que sabe me aconseje y los que no saben puedan aprender de mis exitos y derrotas. 


Bueno, todo empezó con un curso de scala de coursera y quede encantado con el lenguaje y la programación funcional. De a poco tambien me interiorice en otros lenguajes como erlang, haskell, Clojure, Elixir, F#, etc ... pero siempre en la volvía a Scala por gusto nomas. Y me iba bastante bien...


Hasta que empece a quedarme corto en scala, quería progresar y decidí ver algunos framework funcionales, leí un libro de Cats y la verdad es que si bien esta bueno, me frustre mucho... porque no sabia porque se hacían ciertas cosas, no entendía porque tan complejo o si bien entendía desconfiaba ( no hay otra forma de hacerlo más fácil) y bueno ... 


Cambie a Akka, y me pareció bueno y me sentí más en mi mundo, pero justo justo cuando volvia el entuciasmo, cambio de licencia y bueno... Se fue el entusiasmo, ahora sé que existe un fork open source llamado pekko (https://github.com/apache/incubator-pekko) y es de apache. Pero lo veo verde, y no sé cuanto va a crecer ...


Y ahora estoy con ZIO y por ahora todo va bien, por ende la conclusión es si quieren progresar en su conocimiento en Scala, y no vienen del mundo funcional como Haskell, ZIO esta bueno. 

Si son nuevos en el mundo funcional y scala, yo haría lo siguiente: 

1. Estudiar Scala (curso de scala de coursera, el libro rojo de programación funcional en scala, etc ...)

2. Leer y estudiar ZIO 

3. Leer y estudiar Cats


Pero no perder de vista Pekko, para ver como progresa. 


Que opinan? 


El ecosistema de Rust


El ecosistema de Rust consta de una serie de herramientas, de las cuales las principales son:

  • rustc: el compilador de Rust que convierte los archivos .rs en binarios y otros formatos intermedios.
  • cargo: el administrador de dependencias de Rust y la herramienta de compilación. Cargo sabe cómo descargar dependencias alojadas en https://crates.io y las pasará a rustc cuando construyas el proyecto. Cargo también viene con un runtime de prueba incorporado que se utiliza para ejecutar pruebas unitarias.
  • rustup: el instalador y actualizador de la cadena de herramientas de Rust. Esta herramienta se usa para instalar y actualizar rustc y cargo cuando se lanzan nuevas versiones de Rust. Además, rustup también puede descargar documentación para la biblioteca estándar. Puede tener varias versiones de Rust instaladas a la vez y rustup le permitirá cambiar entre ellas según sea necesario.


domingo, 4 de junio de 2023

Primeros pasos con ZIO parte 3


Vamos a crear efectos funcionales de ZIO, y podemos hacerlo a partir de valores, cálculos y tipos de datos comunes de Scala.

Usando el método ZIO.succeed, se puede crear un efecto que, cuando se ejecute, tendrá éxito con el valor especificado:

val s1 = ZIO.succeed(42)

El método de succeed toma un parámetro, que garantiza que si le pasa al método algún código para ejecutar, este código se almacenará dentro del efecto ZIO para que ZIO pueda administrarlo y beneficiarse de características como reintentos, tiempos de espera y registro automático de errores.

Usando el método ZIO.fail, puede crear un efecto que, cuando se ejecuta, fallará con el valor especificado:

val f1 = ZIO.fail("Uh oh!")

Para el tipo de datos ZIO, no hay restricción en el tipo de error. Se puede usar cadenas, excepciones o tipos de datos personalizados.

Podemos usar excepciones :

val f2 = ZIO.fail(new Exception("Uh oh!"))


La biblioteca estándar de Scala contiene varios tipos de datos que se pueden convertir en efectos ZIO.

Un Option se puede convertir en un efecto ZIO usando ZIO.fromOption:


val zoption: IO[Option[Nothing], Int] = ZIO.fromOption(Some(2))


Option[Nothing], lo que significa que si dicho efecto falla, fallará con el valor Nothing (que tiene el tipo Opción[Nothing]).

Puede transformar una falla en algún otro valor de error usando orElseFail, uno de los muchos métodos que proporciona ZIO para la gestión de errores:


val zoption2: ZIO[Any, String, Int] = zoption.orElseFail("It wasn't there!")


ZIO tiene una variedad de otros operadores diseñados para facilitar el manejo de Option. En el siguiente ejemplo, los operadores Some y asSomeError se utilizan para facilitar la interfaz con los métodos que devuelven Option, similar al tipo OptionT en algunas bibliotecas de Scala.


val maybeId: ZIO[Any, Option[Nothing], String] = ZIO.fromOption(Some("abc123"))

def getUser(userId: String): ZIO[Any, Throwable, Option[User]] = ???

def getTeam(teamId: String): ZIO[Any, Throwable, Team] = ???


val result: ZIO[Any, Throwable, Option[(User, Team)]] = (for {

  id   <- maybeId

  user <- getUser(id).some

  team <- getTeam(user.teamId).asSomeError 

} yield (user, team)).unsome 


Un Some se puede convertirse en un efecto ZIO usando ZIO.fromEither:


val zeither: ZIO[Any, Nothing, String] = ZIO.fromEither(Right("Success!"))


El tipo de error del efecto resultante será el del caso Izquierdo (Left), mientras que el tipo de éxito será el del caso Derecho (Right).

Un valor Try se puede convertir en un efecto ZIO usando ZIO.fromTry:


import scala.util.Try


val ztry = ZIO.fromTry(Try(42 / 0))


El tipo de error del efecto resultante siempre será Throwable porque Try solo puede fallar con valores de tipo Throwable.


Un Scala Future se puede convertir en un efecto ZIO usando ZIO.fromFuture:


import scala.concurrent.Future


lazy val future = Future.successful("Hello!")

val zfuture: ZIO[Any, Throwable, String] =  ZIO.fromFuture { implicit ec =>

    future.map(_ => "Goodbye!")

  }


La función pasada a fromFuture recibe un ExecutionContext, que permite a ZIO administrar dónde se ejecuta Future (por supuesto, puede ignorar este ExecutionContext).

El tipo de error del efecto resultante siempre será Throwable, porque los valores Future solo pueden fallar con valores de tipo Throwable.






Vamos a instalar Cargo para programar en Rust


No se si da para un post porque tengo linux, y es solo ejecutar : 

$ sudo apt install cargo rust-src rustfmt

y listo!! 

Si todo salio bien, van a hacer : 

$ cargo --version
cargo 1.65.0

Y se imprime la versión de cargo. 

Vamos a hacer un hola mundo, creemos un proyecto: 

cargo new hello_cargo
cd hello_cargo

En Cargo.toml podemos ver datos del proyecto y las dependencias : 

$ ls
Cargo.toml  src

$ cat Cargo.toml 
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Y podemos ver el código rust en src/main.rs

$ cat src/main.rs
fn main() {
    println!("Hello, world!");
}

Y con cargo build compilamos y cargo run corremos : 

$ cargo build
   Compiling hello_cargo v0.1.0 (...rust/hello_cargo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.87s

$ cargo run 
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/hello_cargo`
Hello, world!

Y listo!! 



viernes, 2 de junio de 2023

La potencia de la programación funcional en Java con Vavr

 


En el mundo del desarrollo de software, los lenguajes funcionales están ganando popularidad debido a su enfoque en la inmutabilidad, la programación declarativa y las operaciones funcionales. Java, siendo un lenguaje orientado a objetos por naturaleza, carece de muchas características propias de los lenguajes funcionales. Sin embargo, gracias al framework Vavr, los desarrolladores de Java pueden aprovechar al máximo los conceptos y las ventajas de la programación funcional. 

Vavr es un framework funcional para Java 8 y superior que proporciona una biblioteca de clases y funciones para facilitar la programación funcional en Java. Ofrece una amplia gama de características, incluyendo tipos inmutables, operaciones funcionales, manejo de errores y mucho más. Vavr se basa en el paradigma funcional y promueve el uso de funciones puras, evitando los efectos secundarios y facilitando la escritura de código más limpio y conciso.

Características principales de Vavr:

  • Tipos inmutables: Vavr proporciona una serie de tipos inmutables, como List, Set, Option, Try, etc. Estos tipos aseguran que los objetos no puedan ser modificados una vez creados, lo que ayuda a prevenir errores y garantiza la consistencia del estado.
  • Operaciones funcionales: Vavr ofrece una amplia gama de operaciones funcionales, como map, filter, reduce, flatMap, etc. Estas operaciones permiten manipular y transformar colecciones de datos de manera elegante y expresiva.
  • Patrones de concurrencia: Vavr ofrece una API concisa y segura para trabajar con concurrencia en Java. Proporciona constructores de hilos, promesas y otras estructuras de datos para facilitar la programación concurrente.
  • Manejo de errores: Vavr ofrece una forma más robusta y funcional de manejar errores en Java. El tipo Try captura excepciones y permite manejarlas de manera más elegante y comprensible.
  • Validaciones: Vavr proporciona una API para realizar validaciones de manera concisa y declarativa. Permite combinar múltiples validaciones y proporciona mensajes de error claros y comprensibles.


Entre las ventajas de usar Vavr tenemos :

  • Código más legible y conciso: Las características funcionales de Vavr permiten escribir código más legible y conciso. El uso de funciones puras y tipos inmutables reduce la complejidad y mejora la mantenibilidad del código.
  • Programación segura: Al evitar los efectos secundarios y promover la inmutabilidad, Vavr ayuda a prevenir errores y garantizar la integridad del código.
  • Compatibilidad con Java existente: Vavr se integra perfectamente con el código Java existente, lo que permite aprovechar las ventajas de la programación funcional sin necesidad de cambiar todo el código base.
  • Curvas de aprendizaje suaves: Si estás familiarizado con conceptos de programación funcional, la curva de aprendizaje de Vavr es bastante suave. Vavr proporciona una API coherente y bien documentada que facilita su adopción.

Vavr es un framework funcional poderoso que proporciona a los desarrolladores de Java las herramientas necesarias para aprovechar al máximo los conceptos de programación funcional. Con su amplia gama de características y su fácil integración con el código Java existente, Vavr puede ayudar a mejorar la calidad del código, la legibilidad y la mantenibilidad de las aplicaciones Java. 


Veamos un ejemplo de lo que podemos hacer con Vavr: 


import io.vavr.collection.List;

import io.vavr.control.Option;


public class VavrExample {


    public static void main(String[] args) {

        // Tipos inmutables

        List<Integer> numbers = List.of(1, 2, 3, 4, 5);

        List<Integer> squaredNumbers = numbers.map(n -> n * n);

        System.out.println(squaredNumbers); // Imprime: List(1, 4, 9, 16, 25)


        // Operaciones funcionales

        int sum = squaredNumbers.reduce((a, b) -> a + b);

        System.out.println("Suma: " + sum); // Imprime: Suma: 55


        // Option: manejo de valores nulos

        Option<String> name = Option.of("John");

        String upperCaseName = name.map(String::toUpperCase)

                                   .getOrElse("N/A");

        System.out.println(upperCaseName); // Imprime: JOHN


        Option<String> nullName = Option.of(null);

        String nullSafeName = nullName.map(String::toUpperCase)

                                      .getOrElse("N/A");

        System.out.println(nullSafeName); // Imprime: N/A

    }

}