Translate

viernes, 13 de febrero de 2026

Inferencia comportamental en Iris


A veces un lenguaje de programación rompe las reglas de lo establecido.

Eso es lo que ocurre con Iris, un lenguaje funcional moderno que se anima a repensar algo que la mayoría de los lenguajes considera “resuelto”: la inferencia de tipos.

Mientras lenguajes como Haskell, Scala o TypeScript utilizan sistemas derivados de Hindley–Milner —que deducen tipos a partir de la forma y las restricciones estáticas del código— Iris propone algo distinto: una inferencia comportamental, donde los tipos no se deducen solo por lo que una expresión es, sino por lo que hace.


Para entender a Iris, conviene empezar por Haskell.

En Haskell, el compilador busca deducir el tipo más general posible de una expresión.

Por ejemplo:

f x = x + 1


El compilador infiere que f :: Num a => a -> a.

Eso significa: f toma algo de tipo a, siempre que a sea una instancia de Num, y devuelve otro a.

La inferencia en Haskell es declarativa: parte de un conjunto de reglas sobre cómo los tipos se combinan.

El compilador no intenta entender qué hace f, sino cómo encaja dentro del sistema de tipos predefinido.


Iris toma otro camino.

Cuando uno escribe algo como:

fn addOne(x) = x + 1


el compilador no busca una clase de tipo Num ni una restricción declarativa.

En cambio, observa el comportamiento del código:

  • nota que x participa en una operación aritmética,
  • deduce que x debe pertenecer a un tipo que sepa sumarse,
  • y propaga esa característica como una capacidad requerida, no como una categoría rígida.


De esta forma, Iris no infiere tipos por forma, sino por rol.

El tipo de x no es una etiqueta estática, sino un conjunto de propiedades comportamentales deducidas del uso.


Este enfoque convierte a Iris en un lenguaje que parece dinámico, pero mantiene seguridad estática.

Por ejemplo, si una función usa un valor solo para imprimirlo, Iris no necesita saber que es un String; basta con que ese valor sepa representarse como texto.

Si más adelante ese mismo valor participa en una suma, el sistema de tipos lo amplía para incluir también la capacidad de actuar como número, sin perder la anterior.


Así, el tipo resultante se vuelve una composición de comportamientos inferidos.

En vez de limitar, los tipos se adaptan al uso que el programa les da.


En Haskell, el tipo Num a => a -> a expresa una restricción declarativa: a debe cumplir las leyes de Num.

En Iris, el tipo inferido sería más bien una descripción semántica: “esta función requiere algo que sepa sumarse.”


La diferencia puede parecer semántica, pero en realidad separa dos filosofías:

  • Haskell razona sobre qué debe cumplirse para que el programa sea válido.
  • Iris razona sobre qué comportamiento se manifiesta en el código.


Haskell clasifica; Iris describe.

El primero etiqueta los valores; el segundo observa lo que hacen.

La inferencia comportamental de Iris puede parecer un experimento académico, pero en realidad apunta a una tendencia profunda: sistemas de tipos que entienden la intención del código, no solo su estructura.

Durante décadas, el tipado estático fue visto como un conjunto de reglas sintácticas para detectar errores antes de ejecutar el programa.

Iris sugiere otra visión: los tipos como modelos del significado del programa.

En lugar de decir “esto es un número”, el compilador podría decir “esto se comporta como algo sumable, imprimible o clonable”.


Ese cambio abre un nuevo horizonte:

  • una inferencia más rica, que se adapta al contexto,
  • una frontera difusa entre lenguajes estáticos y dinámicos,
  • y optimizaciones semánticas, donde el compilador comprende la intención del código.


En última instancia, Iris nos recuerda que los tipos no tienen por qué ser jaulas.

Pueden ser descripciones de comportamiento, espejos del flujo semántico del programa.

Y si el futuro del tipado estático pasa por hacer que las máquinas comprendan el significado del código, quizás Iris esté señalando, silenciosamente, hacia ese horizonte.


Dejo link: https://github.com/connorjacobsen/iris


miércoles, 11 de febrero de 2026

Generar valores aleatorios en Elm



Hasta ahora solo hemos visto comandos para realizar solicitudes HTTP, pero también podemos ordenar otras cosas, como generar valores aleatorios. Vamos a crear una aplicación que tira dados y produce un número aleatorio entre 1 y 6.


import Browser

import Html exposing (..)

import Html.Events exposing (..)

import Random


-- MAIN

main =

  Browser.element

    { init = init

    , update = update

    , subscriptions = subscriptions

    , view = view

    }


-- MODEL

type alias Model =

  { dieFace : Int

  }


init : () -> (Model, Cmd Msg)

init _ =

  ( Model 1

  , Cmd.none

  )


-- UPDATE

type Msg

  = Roll

  | NewFace Int


update : Msg -> Model -> (Model, Cmd Msg)

update msg model =

  case msg of

    Roll ->

      ( model

      , Random.generate NewFace (Random.int 1 6)

      )


    NewFace newFace ->

      ( Model newFace

      , Cmd.none

      )


-- SUBSCRIPTIONS

subscriptions : Model -> Sub Msg

subscriptions model =

  Sub.none


-- VIEW

view : Model -> Html Msg

view model =

  div []

    [ h1 [] [ text (String.fromInt model.dieFace) ]

    , button [ onClick Roll ] [ text "Roll" ]

    ]


La novedad es el comando que se ejecuta en la función de actualización:


Random.generate NewFace (Random.int 1 6)


La generación de valores aleatorios funciona de forma ligeramente diferente a la de lenguajes como JavaScript, Python, Java, etc. ¡Veamos cómo funciona en Elm!


Para ello, utilizamos el paquete elm/random, en particular el módulo Random.

La idea principal es que contamos con un generador aleatorio que describe cómo generar un valor aleatorio. Por ejemplo:


import Random


probability : Random.Generator Float

probability =

  Random.float 0 1


roll : Random.Generator Int

roll =

  Random.int 1 6


usuallyTrue : Random.Generator Bool

usuallyTrue =

  Random.weighted (80, True) [ (20, False) ]


Aquí tenemos tres generadores aleatorios. El generador "roll" indica que producirá un entero, y más específicamente, un entero entre 1 y 6 inclusive. Del mismo modo, el generador "usuallyTrue" indica que producirá un booleano, y más específicamente, será verdadero el 80% de las veces.

La cuestión es que todavía no estamos generando los valores. Simplemente estamos describiendo cómo generarlos. A partir de ahí, se usa "Random.generate" para convertirlo en un comando:

generate : (a -> msg) -> Generator a -> Cmd msg

Al ejecutar el comando, el generador genera un valor que se convierte en un mensaje para la función de actualización. En nuestro ejemplo, el generador genera un valor entre 1 y 6, que se convierte en un mensaje como NewFace 1 o NewFace 4. Eso es todo lo que necesitamos para obtener nuestras tiradas de dados aleatorias, ¡pero los generadores pueden hacer mucho más!

Una vez que tengamos generadores simples como probability y usuallyTrue, podemos empezar a combinarlos con funciones como map3. Imaginemos que queremos crear una máquina tragamonedas sencilla. Podríamos crear un generador como este:


import Random


type Symbol = Cherry | Seven | Bar | Grapes


symbol : Random.Generator Symbol

symbol =

  Random.uniform Cherry [ Seven, Bar, Grapes ]


type alias Spin =

  { one : Symbol

  , two : Symbol

  , three : Symbol

  }


spin : Random.Generator Spin

spin =

  Random.map3 Spin symbol symbol symbol


Primero creamos un símbolo para describir las imágenes que pueden aparecer en la máquina tragamonedas. Luego, creamos un generador aleatorio que genera cada símbolo con la misma probabilidad.

A partir de ahí, usamos map3 para combinarlos en un nuevo generador de giros. Este indica que se deben generar tres símbolos y luego combinarlos para formar un giro.

La idea es que, a partir de pequeños bloques, podemos crear un generador que describe un comportamiento bastante complejo. Y luego, desde nuestra aplicación, solo tenemos que ejecutar algo como Random.generate NewSpin spin para obtener el siguiente valor aleatorio.

lunes, 9 de febrero de 2026

Aprende a crear Agentes de Inteligencia Artificial desde cero con Microsoft


¿Querés entender cómo funcionan los agentes de inteligencia artificial y construir los tuyos propios? Microsoft lanzó un curso gratuito, práctico y de nivel principiante llamado “AI Agents for Beginners” que te lleva de la teoría al código paso a paso. 


¿Qué vas a aprender?

El curso está compuesto por 10 lecciones estructuradas que cubren desde conceptos fundamentales hasta ejemplos prácticos de construcción de agentes de IA. 


Algunos temas incluyen:

  • Introducción a los agentes de IA y casos de uso. 
  • Cómo diseñar y estructurar agentes inteligentes.
  • Explorar frameworks y arquitecturas para agentes. 
  • Patrones de diseño para agentes confiables y eficientes. 
  • Cómo llevar un agente a producción. 

Cada lección incluye texto explicativo, ejemplos de código y videos, por lo que podés empezar sin experiencia previa.


¿Qué herramientas y tecnologías se usan?


El curso integra varios frameworks y servicios de Microsoft para crear y ejecutar agentes de IA, incluyendo: 

  • Microsoft Agent Framework (MAF) – un conjunto de herramientas para construir agentes IA. 
  • Azure AI Agent Service – para desplegar y escalar agentes en la nube. 
  • Semantic Kernel – para diseñar la lógica interna de los agentes. 
  • AutoGen – para facilitar el desarrollo con modelos generativos. 


Además, podrás ejecutar los ejemplos usando Jupyter Notebooks y probar distintos modelos, incluso con opciones gratuitas (como modelos hospedados por GitHub). 

¿Cómo empezar?

  1. Cloná el repositorio oficial en GitHub:   git clone https://github.com/microsoft/ai-agents-for-beginners
  2. Explorá las lecciones según tu ritmo. 
  3. Probá los ejemplos de código en tu entorno local o en plataformas de notebooks. 
  4. Sumate al Discord de Azure AI si necesitás ayuda o querés conectar con otros estudiantes. 


¿Vas a aprovecharlo para tu próximo proyecto de IA? 🤖💡

Dejo link: 

https://github.com/microsoft/ai-agents-for-beginners

sábado, 7 de febrero de 2026

Respaldo y Recuperación (Backup & Recovery)

La recuperación ante fallos depende directamente de la estrategia de respaldo adoptada.

Un buen plan de backups asegura que la información pueda restaurarse ante:

  • Fallos del hardware.

  • Errores humanos.

  • Corrupción de datos.

  • Ataques o pérdida de información.



🔸 Estrategias de Respaldo

Tipo de Backup

Descripción

Ventajas

Desventajas

Full (Completo)

Copia total de la base de datos.

Restauración rápida, simple.

Consumo alto de tiempo y espacio.

Incremental

Copia solo los cambios desde el último backup (de cualquier tipo).

Rápido, eficiente.

Restauración más lenta (requiere varios archivos).

Diferencial

Copia los cambios desde el último backup completo.

Restauración más simple que incremental.

Tamaño intermedio.

📘 Ejemplo (PostgreSQL):

pg_dump -U postgres -F c -f backup_full.dump nombre_bd

📘 Ejemplo (MySQL):

mysqldump -u root -p --single-transaction nombre_bd > backup.sql


🔸 Planes de Recuperación ante Desastres (DRP – Disaster Recovery Plan)

Un DRP define los pasos para restaurar la operación del sistema tras un evento catastrófico.

🧩 Elementos clave de un DRP:

  1. Punto de Recuperación (RPO):
    Cantidad máxima de datos que se puede perder (por ejemplo, 1 hora de transacciones).

  2. Tiempo de Recuperación (RTO):
    Tiempo máximo aceptable para restablecer el servicio.

  3. Ubicaciones redundantes:
    Copias de datos en diferentes regiones o centros de datos.

  4. Automatización de restauración:
    Scripts o herramientas que aceleren el proceso.

📘 Ejemplo:
En AWS, se puede configurar RDS Cross-Region Replication y automated backups para recuperación rápida.


🔸 Automatización de Tareas de Mantenimiento

El mantenimiento preventivo garantiza que la base de datos se mantenga óptima y libre de problemas de rendimiento.

🧱 Tareas comunes:

Tarea

Descripción

Herramienta / Comando

Purga de datos

Eliminar registros obsoletos o temporales

DELETE o PARTITION DROP programado

Reconstrucción de índices

Reorganizar estructuras fragmentadas

REINDEX, ALTER INDEX REBUILD

Actualización de estadísticas

Mantener planes de ejecución óptimos

ANALYZE, UPDATE STATISTICS

Vacuum (PostgreSQL)

Reclaim de espacio y optimización

VACUUM FULL

Backup programado

Ejecución automática de respaldos

cron, pgAgent, SQL Server Agent



🔸 Buenas prácticas de mantenimiento

✅ Automatizar todas las tareas críticas (respaldo, vacuum, reindex).
✅ Probar periódicamente los backups restaurándolos en entornos de prueba.
✅ Monitorear espacio en disco y logs de errores.
✅ Revisar alertas del motor (errores de I/O, locks, estadísticas viejas).

viernes, 6 de febrero de 2026

Seguridad y Mantenimiento de la Base de Datos

La seguridad es uno de los pilares más importantes de la administración de bases de datos.
El objetivo es proteger los datos contra accesos no autorizados, evitar ataques y preservar la integridad de la información.

Autenticación y Autorización (Roles y Permisos)

Autenticación

Proceso de verificar la identidad del usuario que intenta acceder a la base de datos.

Ejemplo:
En PostgreSQL:

CREATE USER app_user WITH PASSWORD 'segura123';

En MySQL:

CREATE USER app_user'@'localhost' IDENTIFIED BY 'segura123';

Buenas prácticas:

  • Usar contraseñas robustas.

  • Implementar autenticación integrada (LDAP, Active Directory, IAM).

  • Evitar el uso de cuentas compartidas.


Autorización

Define qué puede hacer cada usuario dentro del sistema, mediante roles y permisos.

Ejemplo:

CREATE ROLE ventas;

GRANT SELECT, INSERT ON pedidos TO ventas;

GRANT ventas TO app_user;

Principio del mínimo privilegio:
Otorgar solo los permisos estrictamente necesarios.



Tipos de permisos más comunes

Tipo

Descripción

Ejemplo

SELECT

Leer datos

GRANT SELECT ON clientes TO usuario;

INSERT

Agregar registros

GRANT INSERT ON pedidos TO usuario;

UPDATE

Modificar registros

GRANT UPDATE ON productos TO usuario;

DELETE

Eliminar registros

GRANT DELETE ON pedidos TO usuario;

EXECUTE

Ejecutar funciones o procedimientos

GRANT EXECUTE ON sp_backup TO admin;


Inyección SQL: Prevención

La inyección SQL (SQL Injection) es uno de los ataques más frecuentes en aplicaciones conectadas a bases de datos.
Consiste en insertar código SQL malicioso en campos de entrada para manipular consultas.

Ejemplo de ataque:

-- Entrada del usuario

' OR 1=1 --


-- Consulta final vulnerable

SELECT * FROM usuarios WHERE nombre = '' OR 1=1 --' AND clave = '';

Esto devuelve todos los usuarios.


Prevención

  1. Usar consultas parametrizadas o prepared statements.


# Ejemplo en Python con psycopg2

cur.execute("SELECT * FROM usuarios WHERE nombre = %s AND clave = %s", (user, password))

  1. Validar y sanitizar entradas.

    • Aceptar solo tipos esperados (números, fechas, correos, etc.).

    • Rechazar caracteres especiales o comillas innecesarias.

  2. Evitar concatenar strings en consultas.

  3. Restringir permisos del usuario de conexión.

    • Que el usuario de la aplicación tenga solo permisos de lectura/escritura, no de administración.


Cifrado de Datos Sensibles

El cifrado protege la confidencialidad de la información, incluso si el almacenamiento físico es comprometido.

1. Cifrado en Reposo

Protege los datos almacenados en disco (archivos, backups, logs).

Ejemplo:

  • PostgreSQL + pgcrypto:


SELECT pgp_sym_encrypt('123456789', 'claveSecreta');

SELECT pgp_sym_decrypt(columna_cifrada, 'claveSecreta');

  • MySQL:


SELECT AES_ENCRYPT('123456789', 'clave');

SELECT AES_DECRYPT(campo, 'clave');

2. Cifrado en Tránsito

  • Usar SSL/TLS para proteger las conexiones.

  • Configurar los clientes con sslmode=require.

3. Cifrado a nivel de columna o campo

Ideal para datos como contraseñas, documentos, tarjetas o DNIs.

Consejo:

Usar funciones de hash (como bcrypt o SHA-256) para contraseñas, no cifrado reversible.