Translate

martes, 17 de junio de 2025

Otra vez, la Arquitectura Elm

 Se acuerdan de este post, puede que te resulte más fácil ver cómo encajan en el diagrama que vimos en otro post anterior:


Elm comienza mostrando el valor inicial en pantalla. A partir de ahí, entras en este bucle:

  1. Esperar la entrada del usuario.
  2. Enviar un mensaje para actualizar.
  3. Producir un nuevo modelo.
  4. Llamar a la vista para obtener el nuevo HTML.
  5. Mostrar el nuevo HTML en pantalla.
  6. ¡Repetir!

Esta es la esencia de la arquitectura Elm. Cada ejemplo que veamos a partir de ahora será una ligera variación de este patrón básico.


lunes, 16 de junio de 2025

¿Qué es fixed en C#?


La palabra clave fixed en C# se usa en dos contextos, ambos relacionados con el trabajo de bajo nivel y el manejo de memoria no administrada (unsafe code).


Para obtener un puntero fijo a una variable administrada

En C#, los objetos administrados pueden moverse en memoria (por el Garbage Collector). Si queremos trabajar con punteros (como en C o C++), necesitamos asegurarnos de que el objeto no se mueva durante el uso del puntero.

Ahí entra fixed, le indica al runtime que fije el objeto en memoria.


unsafe 

{

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


    fixed (int* p = numeros)

    {

        for (int i = 0; i < 4; i++)

        {

            Console.WriteLine(*(p + i));

        }

    }

}


  • fixed bloquea el arreglo numeros en memoria.
  • int* p es un puntero al primer elemento.
  • Mientras esté dentro del bloque fixed, numeros no se moverá.


Para fijar buffers dentro de structs fixed size buffer

C# permite declarar buffers de tamaño fijo dentro de structs cuando estás trabajando en código unsafe.


unsafe struct MiBuffer

{

    public fixed byte datos[10];

}


  • Aquí datos es un array de 10 bytes directamente dentro del struct.
  • Es muy usado cuando interoperás con código nativo o estructuras de bajo nivel.


Cosas importantes:

  • Solo se puede usar fixed dentro de código marcado como unsafe.
  • Necesitás habilitar la opción /unsafe en el compilador o el flag en el proyecto.
  • Ayuda a interoperar con APIs nativas, como Win32, o trabajar con bloques de memoria directamente.
  • No es algo común en código de negocio habitual en C#, pero sí muy usado en interop, performance crítica o trabajo con hardware.



Arrow Function vs Function en JavaScript


En JavaScript tenemos dos formas principales de declarar funciones:


// Function clásica

function sumar(a, b) {

  return a + b;

}


// Arrow function

const sumar = (a, b) => a + b;


Aunque pueden parecer equivalentes, tienen diferencias importantes:

this léxico:

  • Function clásica: el this depende de cómo se invoque la función.
  • Arrow function: el this se captura del contexto donde fue definida.


const obj = {

  numero: 42,

  funcionClasica: function() {

    console.log(this.numero);  // 42

  },

  arrowFunction: () => {

    console.log(this.numero);  // undefined (o distinto según el contexto externo)

  }

}


Las arrow functions no tienen su propio this.

Las arrow functions no tienen arguments:


function funcionClasica() {

  console.log(arguments);

}


const arrowFunction = () => {

  console.log(arguments); // ReferenceError

}


Si necesitas los argumentos en arrow function, podés usar parámetros rest (...args).


Las arrow functions no pueden ser constructores:


No podés hacer new sobre una arrow function.


function Persona() {}

const PersonaArrow = () => {};


new Persona(); // OK

new PersonaArrow(); // TypeError


Las arrow functions no tienen prototype:


console.log(Persona.prototype); // {}

console.log(PersonaArrow.prototype); // undefined


Las arrow functions tienen una sintaxis más compacta. Ideal para callbacks, funciones de orden superior o programación funcional:


const numeros = [1, 2, 3];

const dobles = numeros.map(n => n * 2);


Hoisting:

  • Las function declarations se elevan (hoisting).
  • Las arrow functions (al ser expresiones) no:


sumar(2,3); // OK

function sumar(a,b){ return a + b; }


sumarArrow(2,3); // TypeError: sumarArrow is not a function

const sumarArrow = (a,b) => a + b;


Mejor en callbacks, no siempre en métodos:


Por su manejo de this, las arrow functions suelen ser preferidas en callbacks, pero no tanto para métodos de objetos o clases.


Regla rápida: usa arrow functions cuando quieras mantener el this del contexto externo (sobre todo en callbacks). Usa functions clásicas cuando necesites un constructor, arguments, o una función completamente autónoma.


domingo, 15 de junio de 2025

¿Por qué C++ necesita punteros para implementar polimorfismo?


En muchos lenguajes de programación orientados a objetos, como Java, C#, Python o Ruby, el polimorfismo funciona de forma "natural" al manejar los objetos por referencia. Sin embargo, cuando trabajamos con C++, rápidamente notamos un detalle que desconcierta a muchos: para obtener polimorfismo dinámico necesitamos punteros (o referencias). ¿Por qué ocurre esto? Vamos a desglosarlo.

El polimorfismo permite invocar métodos sobre objetos de clases derivadas a través de una referencia o puntero a la clase base. El método invocado es el de la clase real del objeto, no necesariamente el de la clase base.

En C++, esto se logra declarando métodos virtual en la clase base:


class Animal {

public:

    virtual void hablar() {

        std::cout << "Soy un animal" << std::endl;

    }

};


class Perro : public Animal {

public:

    void hablar() override {

        std::cout << "Guau" << std::endl;

    }

};


Ahora viene la parte clave.


Animal a = Perro();

a.hablar();  // Imprime: "Soy un animal"


A pesar de haber creado un Perro, el método hablar de Animal es el que se llama. ¿Por qué?

Porque en esta asignación:


Animal a = Perro();


se produce slicing ("corte de objeto"). Solo la parte Animal del Perro es copiada en el objeto a. El comportamiento dinámico queda perdido.


Perro p;

Animal* a = &p;

a->hablar();  // Imprime: "Guau"


Ahora sí, el método correcto se invoca. ¿Qué cambió? Simple: no hay slicing. Estamos trabajando con la dirección de memoria del objeto original, donde toda la jerarquía de clases está intacta.

La razón está en el modelo de objetos de C++:

  • C++ permite que los objetos vivan por valor. Si asignamos Animal a = Perro(), se copia sólo la parte visible desde Animal. Las partes de Perro no existen en esa copia.
  • Los lenguajes como Java o C# manejan los objetos siempre por referencia; nunca se copia el objeto en una asignación como `Animal a = new Perro()`. Por eso ahí el slicing no ocurre.


Cuando usamos punteros o referencias en C++:

  • No se copia el objeto.
  • Se mantiene la dirección al objeto original.
  • El compilador puede usar el vtable (tabla virtual) para despachar la llamada al método correcto.

Una de las ventajas de C++ es que te permite elegir:

  • Si quieres performance máxima (sin polimorfismo), puedes trabajar por valor.
  • Si quieres polimorfismo dinámico, debes trabajar por puntero o referencia.


Este control es una de las razones por las cuales C++ sigue siendo muy usado en sistemas embebidos, motores de juego, drivers, etc.

C++ también permite otras formas de polimorfismo (estático), como el basado en plantillas (CRTP, SFINAE, concepts en C++20), donde no necesitamos punteros porque el binding ocurre en tiempo de compilación.


miércoles, 11 de junio de 2025

Ejemplo de una Aplicación en Elm


Nuestro primer ejemplo es un contador que se puede incrementar o decrementar:


-- Press buttons to increment and decrement a counter.

--

-- Read how it works:

--   https://guide.elm-lang.org/architecture/buttons.html

--



import Browser

import Html exposing (Html, button, div, text)

import Html.Events exposing (onClick)




-- MAIN

main =

  Browser.sandbox { init = init, update = update, view = view }




-- MODEL

type alias Model = Int

init : Model

init =  0




-- UPDATE

type Msg  = Increment  | Decrement


update : Msg -> Model -> Model

update msg model =

  case msg of

    Increment ->  model + 1

    Decrement ->  model - 1


-- VIEW

view : Model -> Html Msg

view model =  div []

    [ button [ onClick Decrement ] [ text "-" ]

    , div [] [ text (String.fromInt model) ]

    , button [ onClick Increment ] [ text "+" ]

    ]


Ahora que has explorado un poco el código, puede que tengas algunas preguntas. ¿Qué hace el valor principal? ¿Cómo se integran las diferentes partes? Analicemos el código y hablemos de ello.

El código utiliza anotaciones de tipo, alias de tipo y tipos personalizados. El objetivo de esta sección es familiarizarse con la arquitectura de Elm, así que no los abordaremos hasta más adelante. 

El main es especial en Elm. Describe lo que se muestra en pantalla. En este caso, inicializaremos nuestra aplicación con el valor init, la función de vista mostrará todo en pantalla y la entrada del usuario se introducirá en la función de actualización. Considérelo la descripción general de nuestro programa.

El modelado de datos es fundamental en Elm. El objetivo del modelo es capturar todos los detalles de la aplicación como datos.

Para crear un contador, necesitamos registrar un número que sube y baja. Esto significa que nuestro modelo es muy pequeño esta vez:


type alias Model = Int


Solo necesitamos un valor entero para registrar el conteo actual. Podemos verlo en nuestro valor inicial:


init : Model

init =  0


El valor inicial es cero y aumentará y disminuirá a medida que las personas presionen diferentes botones.

Tenemos un modelo, pero ¿cómo lo mostramos en pantalla? Esa es la función de vista:


view : Model -> Html Msg

view model =

  div []

    [ button [ onClick Decrement ] [ text "-" ]

    , div [] [ text (String.fromInt model) ]

    , button [ onClick Increment ] [ text "+" ]

    ]


Esta función toma el Modelo como argumento. Genera HTML. Por lo tanto, indicamos que queremos mostrar un botón de decremento, el conteo actual y un botón de incremento.


Observa que tenemos un controlador "onClick" para cada botón. Este indica que, al hacer clic, se genera un mensaje. El botón "+" genera un mensaje de incremento. ¿Qué es y adónde va? ¡A la función de actualización!

La función de actualización describe cómo cambiará nuestro modelo con el tiempo.


Definimos dos mensajes que podría recibir:


type Msg = Increment | Decrement


A partir de ahí, la función de actualización simplemente describe qué hacer cuando recibes uno de estos mensajes.


update : Msg -> Model -> Model

update msg model =

  case msg of

    Increment ->

      model + 1

    Decrement ->

      model - 1


Si recibe un mensaje de Incremento, incrementa el modelo. Si recibe un mensaje de Decremento, lo decrementa.


Así que, cada vez que recibimos un mensaje, lo ejecutamos mediante la función de actualización para obtener un nuevo modelo. Luego, llamamos a la vista para determinar cómo mostrar el nuevo modelo en pantalla. ¡Y luego repetimos! La entrada del usuario genera un mensaje, actualiza el modelo, lo visualiza en pantalla, etc.





martes, 10 de junio de 2025

new vs override en C#: ¿cuál es la diferencia y cuándo usar cada uno?



Cuando trabajamos con herencia en C#, dos palabras clave aparecen para definir comportamiento en clases derivadas: new y override. A primera vista pueden parecer similares, pero representan intenciones diferentes. Entenderlas bien puede evitarte muchos errores.

En C# una clase puede heredar miembros (métodos, propiedades, etc.) de otra. ¿Pero qué pasa si queremos cambiar el comportamiento de un método heredado?

Ahí entran en juego override y new.

Cuando queremos modificar el comportamiento de un método heredado, usámos override.


class Animal

{

    public virtual void Hablar()

    {

        Console.WriteLine("Hace un sonido");

    }

}


class Perro : Animal

{

    public override void Hablar()

    {

        Console.WriteLine("Guau");

    }

}


Y ahora mirá esto:


Animal a = new Perro();

a.Hablar(); // Imprime "Guau"


Con override, el comportamiento depende del tipo real del objeto, no del tipo de la variable. Esto se llama polimorfismo.


En cambio, new oculta el método de la clase base. No lo reemplaza a nivel del sistema de tipos.


class Animal

{

    public void Hablar()

    {

        Console.WriteLine("Hace un sonido");

    }

}


class Gato : Animal

{

    public new void Hablar()

    {

        Console.WriteLine("Miau");

    }

}


Probá esto:

Animal a = new Gato();

a.Hablar(); // Imprime "Hace un sonido"


Porque a es del tipo Animal, y el método Hablar() de Gato no lo reemplazó: solo lo ocultó. Pero:


Gato g = new Gato();

g.Hablar(); // Imprime "Miau"


¿cuándo uso new?

  • Cuando querés ocultar intencionalmente un método de la clase base, pero sin reemplazarlo para el sistema de tipos.
  • Generalmente se evita, porque puede causar confusión. Se usa cuando no podés cambiar la clase base (por ejemplo, una librería externa) pero necesitás un método con el mismo nombre y diferente comportamiento.


¿Qué pasa si no usás ni new ni override? El compilador mostrará una advertencia si detecta que estás ocultando un miembro sin decirlo explícitamente.


// Esto compila, pero da warning:

public void Hablar() { ... } // Oculta un método de la base


Siempre que ocultes un miembro heredado, C# quiere que lo declares con new o override, para que tu intención sea clara.

domingo, 8 de junio de 2025

La Arquitectura Elm

La Arquitectura Elm es un patrón para diseñar programas interactivos, como aplicaciones web y juegos.

Esta arquitectura parece surgir de forma natural en Elm. En lugar de que alguien la inventara, los primeros programadores de Elm seguían descubriendo los mismos patrones básicos en su código. ¡Era un poco inquietante ver a gente terminar con código bien diseñado sin planificación previa!

Así que la Arquitectura Elm es sencilla en Elm, pero útil en cualquier proyecto front-end. De hecho, proyectos como Redux se han inspirado en la Arquitectura Elm, así que quizás ya hayas visto derivados de este patrón. La cuestión es que, incluso si aún no puedes usar Elm en el trabajo, obtendrás mucho de su uso e internalización.

Los programas Elm siempre se ven así:


El programa Elm produce HTML para mostrar en pantalla y luego el ordenador envía mensajes de respuesta sobre lo que está sucediendo. "¡Han pulsado un botón!"

Pero ¿qué sucede dentro del programa Elm? Siempre se divide en tres partes:

  • Modelo: el estado de la aplicación
  • Vista: una forma de convertir el estado a HTML
  • Actualización: una forma de actualizar el estado según los mensajes

Estos tres conceptos son la base de la arquitectura Elm.


jueves, 5 de junio de 2025

Proxy en javascript


En JavaScript moderno hay varias formas de observar cambios en objetos, pero la más directa y nativa es usando Proxy, que te permite interceptar y reaccionar a operaciones sobre un objeto, como modificar una propiedad.


Veamos un ejemplo:


function observeObject(obj, callback) {

  return new Proxy(obj, {

    set(target, prop, value) {

      const oldValue = target[prop];

      target[prop] = value;


      // Ejecutamos el callback cuando se modifica algo

      callback(prop, value, oldValue);


      return true;

    }

  });

}


// Ejemplo de uso

const original = { nombre: 'Juan', edad: 30 };


const observado = observeObject(original, (prop, newVal, oldVal) => {

  console.log(`Propiedad '${prop}' cambió de ${oldVal} a ${newVal}`);

});


observado.nombre = 'Ana';   // Se dispara el callback

observado.edad = 31;        // También se dispara


Me gusto mucho, es simple y puede venir al pelo para ciertas ocaciones, pero debemos de tener en cuenta que : 

  • Solo observa propiedades directas, no anidadas. Si querés observar objetos dentro del objeto, hay que aplicar recursivamente el Proxy.
  • No detecta borrados (como delete obj.prop) a menos que también implementes el deleteProperty trap.
  • No funciona con arrays de forma intuitiva si hacés push, pop, etc., salvo que interceptes esos métodos.


martes, 3 de junio de 2025

Tuplas en Elm


Las tuplas son otra estructura de datos útil. Una tupla puede contener dos o tres valores, y cada valor puede ser de cualquier tipo. Un uso común es cuando se necesita devolver más de un valor de una función. La siguiente función recibe un nombre y envía un mensaje al usuario:

> isGoodName name =

|   if String.length name <= 20 then

|     (True, "name accepted!")

|   else

|     (False, "name was too long; please limit it to 20 characters")

<function>


> isGoodName "Tom"

(True, "name accepted!")



>  

domingo, 1 de junio de 2025

Listas en Elm


Las listas son una de las estructuras de datos más comunes en Elm. Contienen una secuencia de elementos relacionados, de forma similar a los arrays en JavaScript.

Las listas pueden contener muchos valores. Todos estos valores deben ser del mismo tipo. A continuación, se muestran algunos ejemplos que utilizan funciones del módulo Lista:


> names =

|   [ "Alice", "Bob", "Chuck" ]

["Alice","Bob","Chuck"]


> List.isEmpty names

False

> List.length names

3

> List.reverse names

["Chuck","Bob","Alice"]

> numbers =

|   [4,3,2,1]

[4,3,2,1]

> List.sort numbers

[1,2,3,4]

> increment n =

|   n + 1

<function>

> List.map increment numbers

[5,4,3,2]


¡todos los elementos de la lista deben tener el mismo tipo!

  

If en ELM


Para lograr un comportamiento condicional en Elm, se usa una expresión if.

Creemos una nueva función de saludo que sea apropiadamente respetuosa con el presidente Abraham Lincoln:


> greet name =

|   if name == "Abraham Lincoln" then

|     "Greetings Mr. President!"

|   else

|     "Hey!"

<function>

> greet "Tom"

"Hey!"

> greet "Abraham Lincoln"

"Greetings Mr. President!"