Translate

sábado, 26 de abril de 2025

Nuevas características de C# 13


C# 13, junto con .NET 9, trae una serie de mejoras que buscan simplificar la escritura de código, mejorar el rendimiento y ofrecer mayor flexibilidad a los desarrolladores. Veamos que trajo:

Colecciones params mejoradas

Ahora, el modificador params se puede aplicar a tipos que admiten inicialización mediante expresiones de colección, no solo a arreglos. Esto permite una mayor flexibilidad al definir métodos que aceptan un número variable de argumentos.


public void EscribirNombres(params List<string> nombres)

    => Console.WriteLine(string.Join(", ", nombres));


Nuevo tipo de bloqueo: System.Threading.Lock

Se introduce un nuevo tipo de bloqueo que mejora la sincronización de subprocesos. A diferencia del uso tradicional de lock con objetos, System.Threading.Lock proporciona una API más clara y segura para manejar exclusiones mutuas.


var myLock = new Lock();

using (myLock.EnterScope())

{

    // Código protegido

}


Nueva secuencia de escape: \e

Se añade la secuencia de escape \e para representar el carácter de escape ASCII (U+001B). Anteriormente, se utilizaban \u001b o \x1b, pero esta nueva notación es más clara y evita ambigüedades.


Tipos naturales de grupo de métodos

Se mejora la inferencia de tipos al trabajar con grupos de métodos, permitiendo que el compilador determine de manera más precisa el tipo adecuado en contextos donde se utilizan delegados o expresiones lambda.


Propiedades e indexadores parciales

Al igual que los métodos parciales, ahora es posible declarar propiedades e indexadores parciales. Esto facilita la separación de la declaración y la implementación, especialmente útil al trabajar con generadores de código.


partial class MiClase

{

    public partial int MiPropiedad { get; set; }

}


Acceso implícito a indexadores en inicializadores de objetos

Se permite el uso de índices implícitos, como [^1], dentro de inicializadores de objetos, lo que simplifica la inicialización de colecciones y estructuras de datos complejas.


var miArreglo = new int[5] { [^1] = 10 };


Soporte para ref struct en interfaces y genéricos

Los tipos ref struct ahora pueden implementar interfaces y ser utilizados como argumentos de tipo en genéricos, ampliando su aplicabilidad en escenarios de alto rendimiento y manipulación de memoria.


Variables ref y contextos unsafe en iteradores y métodos asincrónicos

Se habilita el uso de variables ref locales y contextos unsafe dentro de métodos asincrónicos y iteradores, siempre que se cumplan ciertas restricciones para garantizar la seguridad del código.


Atributo OverloadResolutionPriority

Se introduce el atributo OverloadResolutionPriority que permite a los desarrolladores especificar la prioridad de resolución de sobrecargas, facilitando la evolución de las API sin romper compatibilidad con versiones anteriores.

Estas mejoras reflejan el compromiso continuo de la comunidad de C# por evolucionar el lenguaje, haciéndolo más potente y expresivo. 


Dejo link: https://developers.redhat.com/articles/2025/04/16/c-13-new-features#

martes, 22 de abril de 2025

Tipos Genéricos Anónimos en Scala: Wildcards y Subtipado


Antes de empezar este post tal vez sería bueno que leas este post antes :D

Scala permite definir colecciones y estructuras de datos genéricas, pero a veces no necesitamos saber con precisión qué tipo contienen, o simplemente queremos permitir varios tipos relacionados. Para esos casos existen los tipos genéricos anónimos, representados por el underscore (_).

Un tipo genérico anónimo en Scala se escribe con un guion bajo (_) en lugar de especificar un tipo concreto. Es útil cuando:

  • No necesitás conocer el tipo exacto.
  • Queremos aceptar varios subtipos o supertypos.

Veamos un ejemplo:

Lista de cualquier tipo (wildcard total)


val lista: List[_] = List(1, "hola", true)


Esto indica que lista es de algún tipo List[T], pero no importa cuál es T.


Subtipado: _ <: Tipo

Permite aceptar cualquier subtipo del tipo dado (covarianza).


class Animal

class Perro extends Animal

class Gato extends Animal


val animales: List[_ <: Animal] = List(new Perro, new Gato)


Esto significa: una lista de algo que es subtipo de Animal.


Supertyping: _ >: Tipo

Permite aceptar cualquier supertipo del tipo dado (contravarianza).


val cosas: List[_ >: Perro] = List(new Animal, new Perro)


Esto significa: una lista de algo que es supertipo de Perro.


Y ¿Por qué usar genéricos anónimos?

  • Cuando escribís funciones genéricas que pueden aceptar muchos tipos.
  • Para asegurar compatibilidad con estructuras covariantes/contravariantes.
  • Para restringir o abrir el tipo de manera controlada.

Los tipos anónimos no te permiten hacer mucho con los elementos (no podés acceder a sus métodos específicos), porque Scala no sabe exactamente qué tipo hay.


val lista: List[_] = List("hola", "chau")

// lista(0).toUpperCase()  // ERROR: no se puede garantizar el tipo


En Java existe el signo de pregunta para esto (?):


List<?> lista;

List<? extends Animal> subtipos;

List<? super Perro> supertypos;


En Scala es más limpio y expresivo:


List[_]

List[_ <: Animal]

List[_ >: Perro]


Los tipos genéricos anónimos en Scala te permiten trabajar con estructuras de datos más genéricas y flexibles, especialmente en APIs o librerías donde no se necesita o no se conoce el tipo exacto. Son ideales para mantener la seguridad de tipos sin perder generalidad.


Tipos de Herencia en C++


En C++, cuando una clase hereda de otra, podemos especificar cómo se deben heredar los miembros públicos y protegidos de la clase base. Esto se hace mediante el tipo de herencia, que puede ser public, protected o private.

public: Herencia Pública


class Animal {

public:

    void respirar() {}

};


class Perro : public Animal {

    // "respirar" sigue siendo público

};



Es la más común para relaciones del tipo es-un (ej: un perro es un animal). Y queda todo como el padre, los metodos publicos y protejidos siguen publicos y protejidos en el hijo. 


protected: Herencia Protegida


class Perro : protected Animal {

    // "respirar" pasa a ser protegido

};

Esta forma rara vez se usa, pero puede servir para limitar el acceso desde fuera sin perder herencia funcional. Los metodos publicos y protejidos pasan a protejidos en el hijo. 


private: Herencia Privada


class Perro : private Animal {

    // "respirar" pasa a ser privado

};


Es útil cuando querés usar la funcionalidad de la clase base sin exponer su interfaz públicamente. Los metodos publicos y protejidos pasan a privados en el hijo. 


Y si no pongo nada ¿Cuál es el valor por defecto?

El valor por defecto en las clases es private y en los structs public. 


domingo, 20 de abril de 2025

Genéricos en Scala: Covarianza y Contravarianza


Scala es un lenguaje poderoso que combina programación funcional y orientada a objetos. Uno de sus conceptos más sofisticados es el sistema de tipos paramétricos, o genéricos, que permiten escribir código flexible y seguro. 

Los genéricos permiten parametrizar clases, traits y métodos con tipos. Por ejemplo:


class Caja[T](valor: T) {

  def get: T = valor

}


Esta clase puede contener un Int, un String, o cualquier otro tipo.

Imaginemos que tenemos las siguientes clases:


class Animal

class Perro extends Animal


Ahora, supongamos que existe una clase genérica Caja[T]. ¿Debería Caja[Perro] ser un subtipo de Caja[Animal]?


En Scala, la relación de subtipos entre tipos genéricos no se asume automáticamente. Vos decidís explícitamente cómo se comporta con respecto a la varianza.

Scala permite controlar la varianza de un tipo genérico con anotaciones en la declaración del tipo.


Invarianza (sin anotación):

class Caja[T](valor: T)

No hay relación de subtipos entre Caja[Perro] y Caja[Animal].


Covarianza (+T)

class Caja[+T](valor: T)

Caja[Perro] es subtipo de Caja[Animal] si Perro es subtipo de Animal.

Usá covarianza cuando vas a leer datos del tipo genérico, pero no escribir.


Ejemplo:

class ListaCovariante[+A](val cabeza: A)


No podés definir un método como:

def setCabeza(a: A): Unit // ERROR con +A


Porque podría romper la seguridad de tipos.


Contravarianza (-T)


class Procesador[-T] {

  def procesar(t: T): Unit = println("Procesando")

}


Procesador[Animal] es subtipo de Procesador[Perro].

Esto es útil cuando recibís datos del tipo genérico (por ejemplo, funciones o procesadores).

Veamos un ejemplo:


class Animal

class Perro extends Animal

class Gato extends Animal


// Covariante: productor

class CajaProductora[+A](val valor: A)


// Contravariante: consumidor

class Procesador[-A] {

  def procesar(a: A): Unit = println(s"Procesando: $a")

}


Uso:


val perro = new Perro

val gato = new Gato


val caja: CajaProductora[Animal] = new CajaProductora[Perro](perro)

val procesador: Procesador[Perro] = new Procesador[Animal]()


procesador.procesar(perro)


En Scala, los tipos de las funciones son contravariantes en los parámetros y covariantes en el resultado.


val f: Perro => Animal = (a: Animal) => a // válido


¿Por qué? Porque si necesitás una función que acepte Perro, es seguro usar una función que acepte Animal, ya que Animal puede incluir a Perro.


La varianza en Scala te permite expresar con precisión las relaciones de subtipos entre clases genéricas:

  • +T (covariante): útil para leer
  • -T (contravariante): útil para escribir
  • T (invariante): comportamiento por defecto


Comprender este sistema es clave para diseñar APIs robustas, especialmente en programación funcional y colecciones.


jueves, 17 de abril de 2025

Full Stack Open


Le quiero recomendar la página fullstackopen, la cual brinda un curso sobre el desarrollo web moderno. Para no hacerlo tan largo, lo que nos dice esta pagina es lo siguiente: 

¡Aprende React, Redux, Node.js, MongoDB, GraphQL y TypeScript de una sola vez! Este curso te presentará el desarrollo web moderno basado en JavaScript. El enfoque principal es crear aplicaciones de una sola página con ReactJS que usando REST API creadas con Node.js.


Dejo link: https://fullstackopen.com/es/

Covarianza y contravarianza en C++


La covarianza y contravarianza en C++ con templates es un poco más manual que en otros lenguajes como Java o C#, porque C++ no tiene soporte directo para la varianza en plantillas. Pero hay formas de simularla o controlarla explícitamente usando herencia, punteros, referencias, std::variant, std::any, SFINAE y concepts.

Primero: ¿Qué es covarianza y contravarianza?

  • Covarianza: cuando un tipo genérico Container<Derived> puede ser tratado como Container<Base>.
  • Contravarianza: lo contrario Container<Base> puede ser usado donde se espera Container<Derived>.
  • Invarianza: no se permite ninguna conversión.


Las Plantillas en C++ son invariantes, esto no es válido:


template <typename T>

class Caja<T> {};


class Animal {};

class Perro : public Animal {};


Caja<Perro>* cp = new Caja<Perro>();

Caja<Animal>* ca = cp;  //  ERROR: no se permite, son tipos distintos


Y listo, fin del post ... Pero pero sí hay covarianza con punteros y referencias a clases (no templates)

Esto funciona:


class Animal {

public:

    virtual void hablar() {}

};


class Perro : public Animal {

public:

    override void hablar() {}

};


Animal* a = new Perro();  // Anda por Covarianza de punteros


¿Cómo resolver o simular varianza con templates?


Usar herencia con punteros o referencias


template <typename T>

class Caja {

public:

    T* valor;

};


Perro* p = new Perro();

Caja<Perro> c1;

c1.valor = p;

Caja<Animal> c2 = c1;  //  No compila directamente

Podemos hacer funciones que trabajen con T* o T& para aprovechar la covarianza de punteros.


Hacer una jerarquía polimórfica manual


class ICajaBase {

public:

    virtual ~ICajaBase() = default;

};


template <typename T>

class Caja : public ICajaBase {

public:

    T valor;

};


Luego podemos trabajar con ICajaBase* y aplicar RTTI o dynamic_cast.


Usar std::variant o std::any para manejar múltiples tipos


#include <variant>

std::variant<Perro, Gato> animal;


Esto evita plantillas covariantes, pero permite flexibilidad en tiempo de ejecución.


Usar std::enable_if, SFINAE o concepts para limitar compatibilidades


template <typename T>

class Caja {

public:

    static_assert(std::is_base_of<Animal, T>::value, "T debe ser un Animal");

};


En C++, las plantillas son invariantes. pero podemos usar covarianza con punteros y referencias a clases base. Si queremos modelar jerarquías con templates, tenemos que hacerlo manualmente usando herencia, punteros, std::variant o SFINAE.

miércoles, 16 de abril de 2025

NativeAOT en .NET


Cuando exploramos el desarrollo moderno en .NET, una de las optimizaciones más atractivas es NativeAOT (Ahead-of-Time Compilation). Esta funcionalidad permite compilar aplicaciones directamente a código nativo antes de que se ejecuten, evitando la compilación Just-In-Time (JIT) y logrando mejoras significativas en el rendimiento.

NativeAOT permite compilar aplicaciones .NET a código máquina nativo al momento del publish, en lugar de hacerlo en tiempo de ejecución (como lo hace el JIT). Esto ofrece beneficios importantes:

  • Tiempo de inicio muy rápido (ideal para CLI, microservicios y funciones serverless).
  • Menor consumo de memoria, ya que no se necesita el motor JIT ni metadatos innecesarios.
  • Sin dependencias externas, se genera un ejecutable autónomo (self-contained).
  • Mejor rendimiento predecible, sin pausas por compilación dinámica.


Pero no todo lo que brilla es oro, como desventajas podemos nombrar:

  • Mayor tiempo de compilación, ya que el análisis y generación de código nativo lleva más procesamiento.
  • Menor compatibilidad con características dinámicas como reflexión, `Assembly.Load`, `Emit`, o serialización automática sin hints.
  • Mayor complejidad en configuración: requiere ajustes específicos en el código y/o hints de runtime.
  • Tamaño del ejecutable potencialmente mayor, dependiendo del trimming y dependencias.

Agregar la propiedad IsAotCompatible en true en el archivo .csproj del proyecto le indica al SDK de .NET que tu aplicación es compatible con NativeAOT. Esto significa que puede ser compilada a código nativo, pero no activa la compilación AOT automáticamente.


<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>

    <OutputType>Exe</OutputType>

    <TargetFramework>net8.0</TargetFramework>

    <IsAotCompatible>true</IsAotCompatible>

  </PropertyGroup>

</Project>


La compilación AOT se activa explícitamente con:


dotnet publish -c Release -r win-x64 /p:PublishAot=true


Aunque NativeAOT ofrece muchas ventajas, no es la opción predeterminada por varias razones:

  • Compatibilidad: La mayoría de las aplicaciones .NET usan características como reflexión, Assembly.Load, o Emit, que no son compatibles con AOT. 
  • Restricciones técnicas: AOT requiere que toda la información esté disponible en tiempo de compilación. Esto implica evitar estructuras dinámicas, y muchas veces obliga a proporcionar hints o metadatos adicionales para que funcione correctamente.
  • Tiempo de compilación: Compilar con AOT puede llevar más tiempo, ya que realiza trimming, análisis y generación de código nativo. Esto puede dificultar el ciclo rápido de desarrollo y prueba.
  • Tamaño del binario: Aunque AOT puede reducir el runtime necesario, a veces el tamaño del ejecutable final puede ser mayor, dependiendo de las dependencias incluidas.

Entonces ¿Cuándo usar NativeAOT?

Es ideal para:

  • Aplicaciones de consola o CLI.
  • Microservicios que buscan iniciar rápido y consumir poca memoria.
  • Apps en contenedores donde se busca eficiencia.
  • Entornos con recursos limitados.
  • Funciones serverless (como Azure Functions, AWS Lambda).


NativeAOT es una herramienta poderosa que puede ofrecer mejoras notables de rendimiento y eficiencia, pero requiere ciertas adaptaciones. 

Si lo pensamos bien, en un mundo donde todas las aplicaciones tienen a correr en el cloud utilizando docker, que una aplicación sea nativa y ocupe menos recursos se vuelve más importante y como la multiplataformidad la brinda docker ya no es tan importante. NativeAOT se vuelve indispensable. 



lunes, 14 de abril de 2025

PEP 750: ¡Template Strings aceptadas oficialmente en Python!


El lenguaje Python sigue evolucionando con propuestas que buscan mejorar la legibilidad, la seguridad y la flexibilidad del código. Una de las más recientes incorporaciones al lenguaje es la PEP 750, que introduce un nuevo sistema de plantillas de texto: Template Strings.

Aunque Python ya cuenta con varias formas de interpolar texto (como `str.format()` o f-strings), esta propuesta agrega una forma más estructurada y segura de construir cadenas dinámicas, especialmente útil en contextos donde el contenido proviene del usuario o debe ser validado y separado del código de negocio.

La idea es inspirarse en los template literals de lenguajes como JavaScript, pero con una sintaxis propia y flexible.

Características clave:

  • Se introduce un nuevo prefijo de cadena: t"" o t'''...''', al estilo de las f-strings (f"").
  • Las expresiones a interpolar deben ser variables ya definidas (sin expresiones arbitrarias como en las f-strings), lo que reduce riesgos de seguridad.
  • Pueden ser útiles para escenarios como generación de HTML, SQL, etc., donde conviene tener una separación estricta entre datos y estructura.


nombre = "Emanuel"

edad = 30


mensaje = t"Hola, {nombre}. Tenés {edad} años."

print(mensaje)  # Hola, Emanuel. Tenés 30 años.


A diferencia de las f"" strings, no podés hacer expresiones como edad + 1, lo que previene comportamientos inesperados y hace el código más declarativo.

Las Template Strings están diseñadas con seguridad en mente. En lugar de permitir cualquier expresión de Python dentro de la cadena (como en f""), restringen la interpolación a nombres de variables válidos. Esto es ideal en:

  • Aplicaciones web (evitar inyecciones accidentales).
  • Motores de plantillas simplificados.
  • Contextos educativos o de scripting seguro.


La PEP 750 ya fue aceptada y se espera su inclusión oficial en una versión futura de Python (posiblemente Python 3.13 si todo va bien).

Dejo link: https://peps.python.org/pep-0750/

viernes, 11 de abril de 2025

JEP 463 : Clases Implícitamente Declaradas y Métodos main de Instancia

La Propuesta de Mejora de Java (JEP) 463, titulada "Clases Implícitamente Declaradas y Métodos main de Instancia", introduce en Java 22 una funcionalidad que simplifica la escritura de programas básicos, especialmente útil para principiantes en el lenguaje.

El propósito principal de esta propuesta es permitir que los estudiantes y nuevos programadores escriban sus primeros programas en Java sin necesidad de comprender características avanzadas del lenguaje. Esto se logra permitiendo la declaración implícita de clases y la utilización de métodos main de instancia, eliminando la necesidad de especificar modificadores como public, static y el parámetro String[] args.

Las características Principales son: 

1. Clases Implícitamente Declaradas: Permiten escribir programas sin declarar explícitamente una clase. El compilador asume una clase implícita que envuelve el código proporcionado.


   void main() {

       System.out.println("Hola, mundo!");

   }


En este caso, no es necesario declarar una clase explícita; el compilador lo maneja automáticamente. 

2. Métodos main de Instancia: Permiten que el método main sea un método de instancia en lugar de un método estático. Esto facilita la introducción de conceptos orientados a objetos de manera más natural para los principiantes.


   void main() {

       System.out.println("Hola desde un método main de instancia!");

   }



Aquí, el método main no requiere ser estático, lo que simplifica la estructura del programa inicial. 

Como beneficios podemos nombrar:

  • Simplicidad: Reduce la cantidad de código ceremonial necesario para escribir programas simples, facilitando el aprendizaje inicial de Java.
  • Progresión Natural: Permite a los estudiantes comenzar con estructuras simples y luego introducir gradualmente conceptos más complejos como clases y métodos estáticos.
  • Compatibilidad: Mantiene la compatibilidad con las características existentes de Java, permitiendo una transición suave hacia programas más complejos.

Es importante destacar que esta funcionalidad se introdujo como una característica en vista previa en Java 21 bajo la JEP 445 y se reintrodujo en Java 22 con la JEP 463, incorporando mejoras basadas en la retroalimentación recibida. Como característica en vista previa, está sujeta a cambios en futuras versiones del lenguaje. 

La JEP 463 representa un avance significativo en la accesibilidad del lenguaje Java para nuevos programadores, simplificando la estructura necesaria para escribir programas básicos y facilitando una curva de aprendizaje más amigable. Al permitir la declaración implícita de clases y métodos main de instancia, Java se vuelve más accesible sin comprometer su robustez para desarrollos más complejos.

Dejo link: https://openjdk.org/jeps/463 

miércoles, 9 de abril de 2025

Cómo crear un Servicio REST en C++


Aunque C++ no es el primer lenguaje que se nos viene a la cabeza cuando hablamos de servicios web, existen varias bibliotecas que nos permiten construir APIs RESTful de manera sencilla y eficiente. En este post te muestro cómo hacerlo utilizando Crow, una de las librerías más populares para este propósito.

Crow es una microframework para C++ similar a Flask (Python) o Express (Node.js). Es ligera, rápida y permite definir rutas y manejar peticiones HTTP con facilidad.

Vamos a necesitar: 

  • Un compilador moderno compatible con C++17 o superior.
  • CMake (versión 3.14 o superior).


Si usás vcpkg podés instalar Crow fácilmente:

vcpkg install crow


O podés clonar el repositorio:


git clone https://github.com/CrowCpp/crow.git


Veamos un hola mundo : 


#include "crow_all.h"


int main()

{

    crow::SimpleApp app;


    CROW_ROUTE(app, "/")([](){

        return "¡Hola, mundo desde C++!";

    });


    CROW_ROUTE(app, "/suma/<int>/<int>")([](int a, int b){

        return crow::response(std::to_string(a + b));

    });


    app.port(18080).multithreaded().run();

}


¿Qué hace este código?

Define dos rutas: `/` y `/suma/{a}/{b}`.

Devuelve texto plano como respuesta.

Usa crow::SimpleApp para levantar un servidor en el puerto `18080`.


Compilamos con CMake


cmake_minimum_required(VERSION 3.14)

project(servicio_rest_cpp)


set(CMAKE_CXX_STANDARD 17)


add_executable(servicio_rest main.cpp)

target_include_directories(servicio_rest PRIVATE /ruta/a/crow)


Luego:

mkdir build && cd build

cmake ..

make

./servicio_rest


Y ya podés acceder a http://localhost:18080/.

C++ no se queda atrás cuando se trata de crear servicios REST. Con herramientas como Crow, podés construir APIs eficientes, con alto rendimiento y mantenerte en el ecosistema de C++. Perfecto para integrarse con sistemas existentes o para casos donde el rendimiento es crítico.


Dejo link: 

https://crowcpp.org/master/


viernes, 4 de abril de 2025

JEP 459: Plantillas de Cadenas en Java


La propuesta de Mejora de Java (JEP) 459, titulada "Plantillas de Cadenas", introduce en Java una nueva funcionalidad que permite la creación de cadenas dinámicas de manera más sencilla y segura. Esta característica, presentada como vista previa en Java 22, complementa las literales de cadena y los bloques de texto existentes al combinar texto literal con expresiones embebidas y procesadores de plantillas para producir resultados especializados. 

Objetivos de JEP 459

  • Simplificar la escritura de programas Java: Facilita la expresión de cadenas que incluyen valores calculados en tiempo de ejecución.
  • Mejorar la legibilidad: Permite una sintaxis más clara al mezclar texto y expresiones, ya sea en una sola línea o en múltiples líneas.
  • Incrementar la seguridad: Al componer cadenas a partir de valores proporcionados por el usuario, se mejora la validación y transformación de las plantillas y sus valores embebidos.
  • Mantener flexibilidad: Permite que las bibliotecas de Java definan la sintaxis de formato utilizada en las plantillas de cadenas.
  • Simplificar el uso de APIs: Facilita la interacción con APIs que aceptan cadenas escritas en lenguajes no Java, como SQL, XML y JSON. 


Las plantillas de cadenas permiten insertar expresiones directamente dentro de cadenas de texto utilizando una sintaxis específica. A continuación, se muestra un ejemplo básico de su uso:


String nombre = "Duke";

String mensaje = STR."Hola, \{nombre}!";

System.out.println(mensaje);


En este ejemplo, `STR` es un procesador de plantillas que evalúa la expresión embebida `\{nombre}` y la reemplaza con su valor correspondiente, produciendo la salida: "Hola, Duke!". 

Java 22 incorpora varios procesadores de plantillas, entre los que se incluyen:

  • STR: Realiza interpolación de cadenas, reemplazando cada expresión embebida con su valor correspondiente convertido a cadena.
  • FMT: Similar a STR, pero acepta especificadores de formato como los definidos en java.util.Formatter, permitiendo un formateo más detallado.
  • RAW`: No procesa automáticamente la plantilla de cadena, útil para crear procesadores de plantillas personalizados.

Es fundamental tener en cuenta que las plantillas de cadenas fueron introducidas como una funcionalidad en vista previa en Java 21 y reintroducidas en Java 22 con JEP 459. Sin embargo, esta característica fue retirada en Java 23 para una revisión y rediseño adicionales, por lo que no está disponible en versiones posteriores.

Aunque actualmente esta característica está en revisión, su implementación futura promete simplificar y asegurar la construcción de cadenas en Java, facilitando la interacción con otros sistemas y mejorando la legibilidad del código.


Dejo link: https://openjdk.org/jeps/459

jueves, 3 de abril de 2025

JEP 456: Variables y Patrones Anónimos en Java



La Propuesta de Mejora de Java (JEP) 456, titulada "Variables y Patrones Anónimos", introduce en Java la capacidad de declarar variables y patrones sin nombre utilizando el carácter de subrayado (_). Esta característica, incorporada en Java 22, permite a los desarrolladores indicar explícitamente que ciertas variables o parámetros no serán utilizados, mejorando así la claridad y mantenibilidad del código. 

En situaciones donde una variable debe ser declarada pero su valor no es necesario, se puede emplear el carácter _ como nombre de la variable. Esto es útil en contextos como bucles for, bloques catch y expresiones lambda.

Veamos un ejemplo en un bucle for:


int[] numeros = {1, 2, 3, 4, 5};

int total = 0;

for (int _ : numeros) {

    total++;

}

System.out.println("Total: " + total);


En este caso, el bucle itera sobre el array numeros sin necesidad de utilizar el valor de cada elemento, incrementando simplemente el contador total.

Veamos un ejemplo en un bloque catch:


try {

    // Código que puede lanzar una IOException

} catch (IOException _) {

    System.out.println("Ocurrió un error de E/S.");

}


Aquí, se captura la excepción IOException sin necesidad de referenciarla dentro del bloque catch. 

Los patrones anónimos permiten omitir nombres y tipos en patrones de registros cuando no se requiere acceder a ciertos componentes. Esto simplifica la sintaxis y mejora la legibilidad del código al desestructurar objetos.


record Punto(int x, int y) {}

Punto punto = new Punto(5, 10);


if (punto instanceof Punto(int _, int y)) {

    System.out.println("Coordenada y: " + y);

}


En este ejemplo, se omite el nombre de la variable para la coordenada x, ya que no es necesaria, mientras que se accede directamente a la coordenada y. 

La introducción de variables y patrones anónimos en Java 22 representa un avance significativo hacia una sintaxis más concisa y expresiva, alineada con las necesidades modernas de desarrollo.

martes, 1 de abril de 2025

Librerías estándar de Python


Python cuenta con una extensa biblioteca estándar que proporciona módulos y funciones listas para usar sin necesidad de instalar paquetes adicionales. A continuación, exploramos algunas de las librerías más útiles y cómo utilizarlas.


Manipulación de Archivos y Directorios


os - Interacción con el sistema operativo

import os

print(os.name)  # Nombre del sistema operativo

print(os.getcwd())  # Directorio actual

os.mkdir("nueva_carpeta")  # Crear una carpeta


shutil - Operaciones de archivos y directorios

import shutil

shutil.copy("archivo.txt", "copia.txt")  # Copiar un archivo

shutil.rmtree("nueva_carpeta")  # Eliminar un directorio


Manejo de Fechas y Tiempos


datetime - Fechas y horas

from datetime import datetime


ahora = datetime.now()

print(ahora.strftime("%Y-%m-%d %H:%M:%S"))  # Formateo de fecha


time - Control de tiempo y pausas

import time


time.sleep(2)  # Pausar ejecución por 2 segundos

print("Dos segundos después...")


Operaciones Matemáticas y Estadísticas


math - Funciones matemáticas


import math


print(math.sqrt(25))  # Raíz cuadrada

print(math.pi)  # Valor de PI


random - Generación de números aleatorios


import random

print(random.randint(1, 100))  # Número aleatorio entre 1 y 100


statistics - Cálculos estadísticos


import statistics


datos = [10, 20, 30, 40, 50]

print(statistics.mean(datos))  # Media

print(statistics.median(datos))  # Mediana


Manejo de Datos en Formato Texto


json - Trabajar con JSON

import json


datos = {"nombre": "Emanuel", "edad": 30}

cadena_json = json.dumps(datos)  # Convertir a JSON

print(json.loads(cadena_json))  # Convertir de JSON a diccionario


csv - Lectura y escritura de archivos CSV

import csv


with open("datos.csv", "w", newline="") as archivo:

    escritor = csv.writer(archivo)

    escritor.writerow(["Nombre", "Edad"])

    escritor.writerow(["Emanuel", 30])


Expresiones Regulares y Manejo de Texto


re - Expresiones regulares


import re


texto = "Correo: usuario@example.com"

patron = r"[\w.-]+@[\w.-]+\.\w+"

resultado = re.search(patron, texto)

print(resultado.group())  # usuario@example.com


Concurrencia y Multiprocesamiento


threading - Programación con hilos


import threading


def tarea():

    print("Ejecutando en un hilo")


hilo = threading.Thread(target=tarea)

hilo.start()


multiprocessing - Procesos en paralelo


import multiprocessing


def tarea():

    print("Ejecutando en un proceso")


proceso = multiprocessing.Process(target=tarea)

proceso.start()


Manejo de Errores y Depuración


logging - Registro de eventos y depuración


import logging


logging.basicConfig(level=logging.INFO)

logging.info("Esto es un mensaje informativo")


La biblioteca estándar de Python proporciona herramientas esenciales para diversas tareas sin necesidad de instalar paquetes adicionales. Conocer y utilizar estos módulos puede hacer que tu código sea más eficiente y organizado.


Novedades de java 22


¡JDK 22 ya está disponible!

JDK 22 ofrece 12 mejoras significativas. Estas mejoras abarcan mejoras en el lenguaje Java, sus API, su rendimiento y las herramientas incluidas en el JDK.

1) Mejoras del lenguaje:

  • Variables y patrones sin nombre - JEP 456: Mejora la legibilidad cuando se requieren declaraciones de variables o patrones anidados, pero no se utilizan. Ambos se indican con el guión bajo.

1.1) Vistas preview del lenguaje

  • Declaraciones antes de super(…) - JEP 447: En los constructores, permite que las declaraciones que no hacen referencia a la instancia que se está creando aparezcan antes de una invocación explícita del constructor.
  • Plantillas de cadena (Segunda versión preliminar) - JEP 459: Las plantillas de cadena complementan los literales de cadena y los bloques de texto existentes de Java al combinar el texto literal con expresiones integradas y procesadores de plantillas para producir resultados especializados.
  • Clases declaradas implícitamente y métodos principales de instancia (Segunda vista previa) - JEP 463: Los estudiantes pueden escribir sus primeros programas en Java sin necesidad de comprender las características del lenguaje diseñadas para programas grandes. En lugar de usar un dialecto independiente del lenguaje, pueden escribir declaraciones simplificadas para programas de una sola clase y luego ampliarlos sin problemas para usar funciones más avanzadas a medida que mejoran sus habilidades.


2) Bibliotecas

  • API de Funciones Externas y Memoria - JEP 454: Permite que los programas Java interoperen con código y datos fuera del entorno de ejecución de Java. Al invocar eficientemente funciones externas (es decir, código fuera de la JVM) y acceder de forma segura a la memoria externa (es decir, memoria no gestionada por la JVM), la API permite que los programas Java invoquen bibliotecas nativas y procesen datos nativos sin la fragilidad ni los riesgos de JNI.


2.1) Vistas previas de bibliotecas e incubadora

  • API de archivo de clase (Vista previa) - JEP 457: Proporciona una API estándar para analizar, generar y transformar archivos de clase Java.
  • Recolectores de streams (Vista previa) - JEP 461: Mejora la API de streams para admitir operaciones intermedias personalizadas. Esto permitirá que las canalizaciones de flujos transformen datos de maneras que no son fáciles de lograr con las operaciones intermedias integradas existentes.
  • Concurrencia estructurada (2.ª vista previa) - JEP 462: Simplifica la programación concurrente. La concurrencia estructurada trata grupos de tareas relacionadas que se ejecutan en diferentes hilos como una sola unidad de trabajo, optimizando así la gestión y cancelación de errores, mejorando la fiabilidad y la observabilidad.
  • Valores con Alcance (2.ª Vista Previa) - JEP 464: Permite compartir eficientemente datos inmutables dentro y entre subprocesos.
  • API Vector (7.ª Incubadora) - JEP 460: Una API para expresar cálculos vectoriales que se compilan de forma fiable en tiempo de ejecución para obtener instrucciones vectoriales óptimas en arquitecturas de CPU compatibles, logrando así un rendimiento superior al de cálculos escalares equivalentes. Este JEP propone reincubar la API en JDK 22, con mejoras menores en la API con respecto a JDK 21. La implementación incluye correcciones de errores y mejoras de rendimiento. Incluimos los siguientes cambios notables: Compatibilidad con el acceso vectorial con segmentos de memoria del montón respaldados por un array de cualquier tipo de elemento primitivo. Anteriormente, el acceso estaba limitado a segmentos de memoria del montón respaldados por un array de bytes.


3) Rendimiento

  • Fijación regional para G1 - JEP 423: Reduce la latencia al implementar la fijación regional en G1, de modo que no es necesario deshabilitar la recolección de elementos no utilizados durante las regiones críticas de la Interfaz Nativa de Java (JNI).


4) Herramientas

Iniciar programas de código fuente con múltiples archivos - JEP 458: Permite a los usuarios ejecutar un programa proporcionado como múltiples archivos de código fuente Java sin tener que compilarlo primero.


Java 22 continúa la evolución del lenguaje, incorporando mejoras que aumentan su expresividad, rendimiento y seguridad. Estas actualizaciones refuerzan el compromiso de la plataforma con la innovación y la adaptabilidad a las necesidades actuales de desarrollo.

En proximos post vamos a detallar las mejoras del lenguaje. 

Dejo link: https://blogs.oracle.com/java/post/the-arrival-of-java-22

Importación de módulos y uso de paquetes en Python


Python es un lenguaje que permite organizar el código en módulos y paquetes, facilitando la reutilización y la modularidad. 

Un módulo en Python es simplemente un archivo `.py` que contiene funciones, clases y variables que pueden ser reutilizadas en otros archivos.


Ejemplo de módulo (`mi_modulo.py`):

Definimos una función en el módulo


def saludar(nombre):

    return f"Hola, {nombre}!"


Para usar un módulo en otro script, se puede importar con import:


import mi_modulo

print(mi_modulo.saludar("Emanuel"))  # Salida: Hola, Emanuel!


También se puede importar solo un elemento específico:


from mi_modulo import saludar

print(saludar("Mundo"))


Si se quiere importar todo el contenido de un módulo pero con un alias:


import mi_modulo as mod

print(mod.saludar("Python"))


Un paquete es una carpeta que contiene múltiples módulos y un archivo especial `__init__.py`, el cual indica que la carpeta debe tratarse como un paquete.


Estructura de un paquete:


mi_paquete/

│── __init__.py

│── modulo1.py

│── modulo2.py



Ejemplo de modulo1.py en mi_paquete:


def sumar(a, b):

    return a + b


Ejemplo de modulo2.py en mi_paquete:


def restar(a, b):

    return a - b


Para importar módulos desde un paquete:


from mi_paquete import modulo1, modulo2

print(modulo1.sumar(5, 3))  # Salida: 8

print(modulo2.restar(10, 4))  # Salida: 6


También se puede importar solo una función específica:


from mi_paquete.modulo1 import sumar

print(sumar(7, 2))


Importación relativa vs. absoluta


- Importación absoluta: Se usa la ruta completa desde el paquete raíz.

 

 from mi_paquete.modulo1 import sumar


- Importación relativa: Se usa `.` o `..` para referirse a módulos dentro del mismo paquete.

 

 from .modulo1 import sumar  # Desde el mismo paquete

 from ..otro_paquete.modulo3 import dividir  # Desde un paquete hermano


Python incluye una gran cantidad de módulos en su biblioteca estándar, que pueden ser importados directamente:


import math

print(math.sqrt(25))  # Salida: 5.0


Para instalar e importar paquetes de terceros (ejemplo: requests):


$ pip install requests



import requests

response = requests.get("https://api.github.com")

print(response.status_code)


El uso de módulos y paquetes en Python permite mantener el código organizado, reutilizable y fácil de mantener. Con las diferentes opciones de importación, puedes estructurar tus proyectos de manera eficiente.