Programación basada en flujo
# Programación basada en flujo

En la programación en computadora, la programación basada en flujo (PBF) es un paradigma de programación que define aplicaciones como redes de procesos de "caja negra", los cuales intercambian datos a través de conexiones predefinidas por pasos de mensajes, donde las conexiones están especificadas externamente a los procesos. Estos procesos de caja negra pueden ser reconectados un sinnúmero de veces para formar diferentes aplicaciones sin tener que ser cambiarlos internamente. PBF, por lo tanto, esta naturalmente orientado a componentes.

PBF es una forma particular de programación de flujo de datos basados en buffers acotados, paquetes de información con tiempos de vida definidos, puertos nombrados y definición separada de conexiones.

## Introducción

La programación basada en flujo define aplicaciones usando la metáfora de una “fábrica de datos”. Ve una aplicación no como un proceso único y secuencial, que comienza en un punto en el tiempo, y luego hace una cosa a la vez hasta que finaliza, sino como una red de procesos asincrónicos que se comunican por medio de flujo de fragmentos de datos, llamados “paquetes de información” (PI). En esta perspectiva, la atención está en los datos de la aplicación y las transformaciones aplicadas para producir los resultados deseados. La red es definida externamente por los procesos, como una lista de conexiones que son interpretadas por una pieza de software, usualmente llamado “programador”.

Los procesos se comunican mediante conexiones de capacidad fija. Una conexión se adjunta a un proceso por medio de un puerto, que tiene un nombre acordado entre el código del proceso y la definición de la red. Más de un proceso puede ejecutar el mismo trozo de código. En cualquier momento, una IP determinada solo puede ser “propiedad” para un solo proceso o estar en tránsito entre dos procesos. Los puertos pueden ser simples o de tipo matriz, como se usa por ejemplo para el puerto de entrada del componente intercalar que se describe a continuación. Es la combinación de puertos con procesos asincrónicos lo que permite que muchas funciones primitivas de procesamientos de datos de larga duración (tales como Ordenar, fusionar, Resumir, etc.) sean soportadas en la forma de cajas negras de software.

Debido a que los procesos PBF pueden continuar ejecutándose siempre que tengan datos para trabajar y en algún lugar colocar su salida, las aplicaciones PBF generalmente se ejecutan en menos lapsos de tiempo que los programas convencionales y hacen un uso óptimo de todos los procesadores en una máquina sin necesidad de programación especial para lograrlo.

La definición de red suele ser esquemática y es convertida en una lista de conexión en algún lenguaje o notación de bajo nivel. PBF es a menudo un lenguaje de programación visual en este nivel. Las definiciones de red más complejas tienen una estructura jerárquica que se constituye a partir de subredes con conexiones “fijas”. Muchos otros lenguajes/tiempos de ejecución basados en el flujo se constituyen alrededor de lenguaje de programación más tradicionales, el ejemplo más notable es RaftLib, que utiliza operadores similares a iostream de C++ para especificar el gráfico de flujo.

PBF tiene mucho en común con el lenguaje Linda en la terminología de Gelernter y Carriero, un “lenguaje de coordinación”: Es esencialmente un lenguaje independiente. De hecho, da un planificador escrito en un lenguaje de nivel suficientemente bajo, Componentes escritos en diferentes lenguajes pueden ser vinculados en una sola red. PBF se presta así al concepto de lenguajes específicos de dominio o “mini lenguajes”.

PBF exhibe “acoplamiento de datos”, descrito en el artículo sobre acoplamiento como el tipo más flojo de acoplamiento entre componentes. El concepto de acoplamiento bajo esta a su vez relacionado con las arquitecturas orientadas al servicio y PBF se ajusta a un número de criterios para dicha arquitectura, aunque en un nivel más detallado que la mayoría de los ejemplos de esta arquitectura.

PFB promueve un estilo funcional de alto nivel de especificaciones que simplifica el razonamiento sobre el comportamiento del sistema. Un ejemplo de esto es el modelo de flujo de datos distribuidos para especificar y analizar constructivamente la semántica de los protocolos distribuidos de múltiples partes.

## Historia

La programación basada en flujo fue inventada por Paul J. Morrison a comienzos de 1970, inicialmente implementó un software para un banco canadiense. PFB en sus inicios fue fuertemente influenciado por algunos lenguajes de simulación de IBM del momento, en particular GPSS, pero sus raíces se remontan al documento seminal de Conway sobre lo que el llamó Corrutinas.

PBF ha sufrido varios cambios de nombre a lo largo de los años: La implementación original se llamaba SPMA (Sistemas de procesamientos modular avanzado). Una gran aplicación en Canadá entró en funcionamiento en 1975 y a partir del 2013, a estado en uso continuo de producción, funcionando diariamente, durante casi 40 años. Debido a que IBM consideraba que las ideas detrás de PBF “se parecían demasiado a una ley de la naturaleza” para ser patentable, en su lugar pusieron los conceptos básicos de PBF en el dominio público, mediante un boletín de divulgación técnica “Sistema de programación de tareas intercaladas, modulares y sensibles a los datos, en 1971. Un artículo describiendo sus conceptos y experiencias de usuarios fue publicado en 1978 en el IBM Research IBM Systems Journal bajo el nombre DSLM. Una segunda implementación fue hecha como un proyecto conjunto de IBM Canadá e IBM Japón, bajo el nombre de “Administrador de desarrollo de flujo de datos” (ADFD) y fue comercializado brevemente en Japón a fines de los años 80 bajo el nombre de “Administrador de programación de flujo de datos”.

Generalmente, los conceptos se denominaron dentro de IBM como “flujo de datos”, pero este término fue considerado demasiado general y finalmente se adoptó el nombre de Programación basada en flujo.

Desde principios de los años 80 hasta 1993, Paul J. Morrison y el arquitecto de IBM Wayne Stevens refinaron y promovieron los conceptos detrás de PBF. Stevens escribió varios artículos describiendo y apoyando los conceptos de PBF e incluyó material en varios de sus libros. [Fuente no primaria necesitada] [fuente no primaria necesitada]. En 1994, Morrison publicó un libro describiendo PBF y proporcionando evidencia empírica de que PBF condujo a tiempos de desarrollo reducidos.

## Conceptos

El siguiente diagrama muestra las principales entidades de un diagrama PBF (aparte de los Paquetes de información). Dicho diagrama se puede convertir directamente en una lista de conexiones, que luego puede ejecutar un motor apropiado (software o hardware).

A, B y C son procesos que ejecutan componentes de código. O1, O2 y las dos entradas son puertos que conectan las conexiones M y N a sus respectivos procesos. Está permitido que los procesos B y C ejecuten el mismo código, por lo que cada proceso debe tener su propio conjunto de almacenamiento de trabajo, bloques de control, etc. Tanto si comparten código como si no, B y C son libres de usar los mismos nombres de puerto, ya que los nombres de puerto solo tienen significado dentro de los componentes que los hacen referencia (y a nivel de red, por supuesto).

M y N son lo que a menudo se denominan "memorias intermedias limitadas" y tienen una capacidad fija en términos de la cantidad de IP que pueden tener en cualquier momento.

El concepto de puertos es lo que permite utilizar el mismo componente en más de un lugar en la red. En combinación con una capacidad de parametrización, llamada Paquetes de información inicial (IIP), los puertos proporcionan a PBF una capacidad de reutilización de componentes, lo que convierte a PBF en una arquitectura basada en componentes . PBF exhibe lo que Raoul de Campo y Nate Edwards de IBM Research han denominado modularidad configurable .

Los paquetes de información o IP se asignan en lo que podría llamarse "espacio de IP" (al igual que las tuplas de Linda se asignan en "espacio de tupla"), y tienen una vida útil bien definida hasta que se eliminan y se recupera su espacio, en PBF esto debe ser una acción explícita por parte de un proceso de propiedad. Las IP que viajan a través de una conexión dada (en realidad son sus "manijas" las que viajan) constituyen un "flujo", que se genera y se consume de forma asíncrona; este concepto tiene similitudes con el concepto de contra perezoso descrito en el artículo de 1976 por Friedman y Wise.

Las IP suelen ser fragmentos estructurados de datos; sin embargo, algunas IP pueden no contener datos reales, sino que se utilizan simplemente como señales. Un ejemplo de esto es "IP de soporte", que se puede utilizar para agrupar IP de datos en patrones secuenciales dentro de una secuencia, llamados "subcorrientes". Las corrientes secundarias pueden a su vez estar anidadas. Las IP también se pueden encadenar para formar "árboles de IP", que viajan a través de la red como objetos individuales.

El sistema de conexiones y procesos descritos anteriormente puede "ramificarse" a cualquier tamaño. Durante el desarrollo de una aplicación, los procesos de monitoreo pueden agregarse entre pares de procesos, los procesos pueden "explotar" en subredes o las simulaciones de procesos pueden reemplazarse por la lógica del proceso real. PBF por lo tanto se presta a la creación rápida de prototipos .

Esta es realmente una imagen de la línea de ensamblaje del procesamiento de datos: las IP que viajan a través de una red de procesos pueden considerarse como widgets que viajan de una estación a otra en una línea de ensamblaje. Las "máquinas" se pueden volver a conectar fácilmente, desconectar para repararlas, reemplazarlas, etc. Por extraño que parezca, esta imagen es muy similar a la del equipo de grabación de la unidad que se usaba para procesar datos antes de los días de las computadoras, excepto que los mazos de tarjetas tenían que llevarse a mano de una máquina a otra.

Las implementaciones de PBF pueden ser no preventivas o preventivas: las implementaciones anteriores tendían a ser no preventivas (mainframe y lenguaje C), mientras que la última implementación de Java (ver a continuación) usa la clase Java Thread y es preventiva.

## Ejemplos

### "Problema del telegrama"

Los componentes de PBF a menudo forman pares complementarios. Este ejemplo usa dos de estos pares. El problema descrito parece muy simple como se describe en palabras, pero de hecho es sorprendentemente difícil de lograr usando la lógica de procedimiento convencional. La tarea, llamada "Problema de Telegram", originalmente descrita por Peter Naur, es escribir un programa que acepte líneas de texto y genere líneas de salida que contengan tantas palabras como sea posible, donde el número de caracteres en cada línea no exceda una cierta longitud. Es posible que las palabras no se dividan y suponemos que ninguna palabra es más larga que el tamaño de las líneas de salida. Esto es análogo al problema de ajuste de palabras en los editores de texto.

En la lógica convencional, el programador descubre rápidamente que ni las estructuras de entrada ni de salida pueden usarse para controlar la jerarquía de llamadas del flujo de control . En PBF, por otro lado, la descripción del problema en sí sugiere una solución:

* Las "palabras" se mencionan explícitamente en la descripción del problema, por lo que es razonable que el diseñador trate las palabras como paquetes de información (IP)
* en PBF no existe una jerarquía de llamadas única, por lo que el programador no está tentado a forzar un subpatrón de la solución para que sea el nivel superior.

Aquí está la solución más natural en PBF (no hay una única solución "correcta" en PBF, pero esto parece un ajuste natural):

donde DC y RC significan "Descomponer" y "Recomponer", respectivamente.

Como se mencionó anteriormente, los Paquetes de información inicial (PII) se pueden usar para especificar información paramétrica, como la longitud de registro de salida deseada (requerida por los dos componentes más a la derecha) o los nombres de archivo. Los PII son fragmentos de datos asociados con un puerto en la definición de red que se convierten en IP "normales" cuando se emite una "recepción" para el puerto correspondiente.

### Actualización de lote

Este tipo de programa implica pasar un archivo de "detalles" (cambios, adiciones y eliminaciones) a un "archivo maestro" y producir (al menos) un archivo maestro actualizado y uno o más informes. Los programas de actualización generalmente son bastante difíciles de codificar utilizando código de procedimiento síncrono, ya que dos (a veces más) secuencias de entrada deben mantenerse sincronizadas, aunque pueden ser maestros sin los detalles correspondientes, o viceversa.

En PBF, un componente reutilizable (Collate), basado en la idea de registro de la unidad de un Collator, hace que escribir este tipo de aplicación sea mucho más fácil ya que Collate fusiona las dos corrientes e inserta el IP de soporte para indicar niveles de agrupación, simplificando significativamente la lógica descendente. Suponga que una secuencia ("maestra" en este caso) consta de un IP con valores clave de 1, 2 y 3, y las IP de la segunda secuencia ("detalles") tienen valores clave de 11, 12, 21, 31, 32, 33 y 41, donde el primer dígito corresponde a los valores de la clave maestra. Al usar caracteres de paréntesis para representar las IP de "paréntesis", la secuencia de salida clasificada será la siguiente:

Como no había un maestro con un valor de 4, el último grupo consta de un solo detalle (más corchetes).

La estructura de la corriente anterior se puede describir sucintamente usando una notación similar a BNN como

Collate es un recuadro negro reutilizable que solo necesita saber dónde están los campos de control en sus IP entrantes (incluso esto no es estrictamente necesario ya que los procesos de transformadores pueden insertarse arriba para colocar los campos de control en ubicaciones estándar), y de hecho pueden generalizarse. a cualquier cantidad de flujos de entrada y cualquier profundidad de anidamiento de paréntesis. Collate utiliza un puerto de tipo matriz para la entrada, lo que permite un número variable de flujos de entrada.

### Procesos de multiplexación

La programación basada en flujo admite la multiplexación de procesos de una manera muy natural. Dado que los componentes son de solo lectura, cualquier número de instancias de un componente dado ("procesos") puede ejecutarse de forma asíncrona entre sí.

Cuando las computadoras usualmente tenían un único procesador, esto era útil cuando ocurrían muchas E / S; ahora que las máquinas suelen tener múltiples procesadores, esto comienza a ser útil cuando los procesos también requieren mucha CPU. El diagrama en esta sección muestra un solo proceso de "Balanceador de carga" que distribuye datos entre 3 procesos, etiquetados como S1, S2 y S3, respectivamente, que son instancias de un solo componente, que a su vez se alimentan en un solo proceso por orden de llegada., primero servido "base.

### Red interactiva simple

En este esquema general, las solicitudes (transacciones) de los usuarios ingresan al diagrama en la esquina superior izquierda y las respuestas se devuelven en la esquina inferior izquierda. Los "back-ends" (en el lado derecho) se comunican con los sistemas en otros sitios, por ejemplo, utilizando CORBA, MQSeries, etc. Las conexiones cruzadas representan solicitudes que no necesitan ir al back-end, o solicitudes que tienen que recorrer la red más de una vez antes de ser devueltas al usuario.

Dado que las diferentes solicitudes pueden usar diferentes back-end, y pueden requerir diferentes cantidades de tiempo para que los back-end (si se usan) los procesen, se debe prever relacionar los datos devueltos con las transacciones solicitantes apropiadas, por ejemplo, tablas hash o cachés.

El diagrama anterior es esquemático en el sentido de que la aplicación final puede contener muchos más procesos: los procesos pueden insertarse entre otros procesos para administrar cachés, mostrar el tráfico de conexión, monitorear el rendimiento, etc. Además, los bloques en el diagrama pueden representar "subredes": redes pequeñas con una o más conexiones abiertas.

## Comparación con otros paradigmas y metodologías.

### Programación estructurada de Jackson (PEJ) y desarrollo del sistema de Jackson (DSJ)

Esta metodología supone que un programa debe ser estructurado como una jerarquía procesal única de subrutinas. Su punto de partida es describir la aplicación como un conjunto de "líneas principales", basadas en las estructuras de datos de entrada y salida. Luego se elige una de estas "líneas principales" para conducir todo el programa, y se requiere que las otras estén "invertidas" para convertirlas en subrutinas (de ahí el nombre de "inversión de Jackson"). Esto a veces da como resultado lo que se llama un "choque", que requiere que el programa se divida en múltiples programas o rutinas. Cuando se utiliza PBF, este proceso de inversión no es necesario, ya que cada componente de PBF puede considerarse una "línea principal" separada.

PBF y PEJ comparten el concepto de tratar un programa (o algunos componentes) como un analizador de un flujo de entrada.

En el trabajo posterior de Jackson, en el desarrollo del sistema de Jackson (DSJ), las ideas se desarrollaron aún más.

En DSJ, el diseño se mantiene como un diseño de red hasta la etapa final de implementación. El modelo luego se transforma en un conjunto de procesos secuenciales para la cantidad de procesadores disponibles. Jackson analiza la posibilidad de ejecutar directamente el modelo de red que existe antes de este paso, en la sección 1.3 de su libro (cursiva agregada):

    La especificación producida al final del paso de sincronización del sistema es, en principio, capaz de ejecución directa. El entorno necesario contendría un procesador para cada proceso, un dispositivo equivalente a un búfer ilimitado para cada flujo de datos y algunos dispositivos de entrada y salida donde el sistema está conectado al mundo real. Tal entorno podría, por supuesto, ser proporcionado por un software adecuado que se ejecute en una máquina lo suficientemente potente. A veces, tal ejecución directa de la especificación será posible, e incluso puede ser una opción razonable.

MA Jackson reconoció la PBF como un enfoque que sigue su método de "Descomposición del programa en procesos secuenciales que se comunican mediante un mecanismo similar a la corutina"

### Programación aplicativa

WB Ackerman define un lenguaje aplicativo como aquel que realiza todo su procesamiento mediante operadores aplicados a valores. El lenguaje aplicativo más antiguo conocido fue LISP.

Un componente de PBF puede considerarse como una función que transforma su (s) flujo (s) de entrada en su (s) flujo (s) de salida. Estas funciones se combinan para hacer transformaciones más complejas, como se muestra aquí:

Si etiquetamos las secuencias, como se muestra, con letras minúsculas, entonces el diagrama anterior se puede representar sucintamente de la siguiente manera:

Al igual que en la notación funcional, F se puede usar dos veces porque solo funciona con valores y, por lo tanto, no tiene efectos secundarios, en la PBF dos instancias de un componente dado pueden ejecutarse simultáneamente entre sí y, por lo tanto, los componentes de la PBF no deben tener efectos secundarios ya sea. La notación funcional podría usarse claramente para representar al menos una parte de una red de PBF.

Entonces surge la pregunta de si los componentes de PBF pueden expresarse mediante notación funcional. WH Burge mostró cómo se pueden desarrollar expresiones de flujo utilizando un estilo de programación recursivo y aplicativo, pero este trabajo fue en términos de (flujos de) valores atómicos. En PBF, es necesario poder describir y procesar fragmentos de datos estructurados (IP de PBF).

Además, la mayoría de los sistemas aplicativos suponen que todos los datos están disponibles en la memoria al mismo tiempo, mientras que las aplicaciones PBF deben ser capaces de procesar flujos de datos de larga duración sin dejar de utilizar recursos finitos. Friedman y Wise sugirieron una forma de hacerlo agregando el concepto de "contras perezosos" al trabajo de Burge. Esto eliminó el requisito de que ambos argumentos de "contras" estén disponibles en el mismo instante de tiempo. Los "contras perezosos" en realidad no crean una secuencia hasta que se dan cuenta de sus dos argumentos, antes de eso simplemente registra una "promesa" de hacer esto. Esto permite que una transmisión se realice dinámicamente desde el frente, pero con un back-end no realizado. El final de la secuencia no se realiza hasta el final del proceso, mientras que el comienzo es una secuencia de elementos es cada vez más larga.

### Linda

Muchos de los conceptos en PBF parecen haber sido descubiertos independientemente en diferentes sistemas a lo largo de los años. Linda, mencionada anteriormente, es uno de esos. La diferencia entre las dos técnicas se ilustra mediante la técnica de equilibrio de carga Linda "escuela de pirañas": en PBF, esto requiere un componente adicional de "equilibrador de carga" que enruta las solicitudes al componente en una lista que tiene el menor número de IP en espera para ser procesado. Claramente, la PBF y Linda están estrechamente relacionadas, y una podría usarse fácilmente para simular a la otra.

### Programación orientada a objetos

Un objeto en POO puede describirse como una unidad semiautónoma que comprende información y comportamiento. Los objetos se comunican por medio de "llamadas de método", que son esencialmente llamadas de subrutina, realizadas indirectamente a través de la clase a la que pertenece el objeto receptor. Solo se puede acceder a los datos internos del objeto mediante llamadas a métodos, por lo que esta es una forma de ocultación de información o "encapsulación". La encapsulación, sin embargo, es anterior a POO: David Parnas escribió uno de los artículos fundamentales sobre ella a principios de los años 70, y es un concepto básico en informática. La encapsulación es la esencia misma de un componente PBF, que puede considerarse como un cuadro negro, que realiza una conversión de sus datos de entrada en sus datos de salida. En la PBF, parte de la especificación de un componente son los formatos de datos y las estructuras de flujo que puede aceptar y los que generará. Esto constituye una forma de diseño por contrato . Además, solo se puede acceder directamente a los datos en una IP mediante el proceso actual. La encapsulación también se puede implementar a nivel de red, haciendo que los procesos externos protejan los internos.

Un artículo de C. Ellis y S. Gibbs distingue entre objetos activos y objetos pasivos. Los objetos pasivos comprenden información y comportamiento, como se indicó anteriormente, pero no pueden determinar el momento de este comportamiento. Los objetos activos, por otro lado, pueden hacer esto. En su artículo, Ellis y Gibbs afirman que los objetos activos tienen mucho más potencial para el desarrollo de sistemas mantenibles que los objetos pasivos. Una aplicación de PBF puede verse como una combinación de estos dos tipos de objetos, donde los procesos PBF corresponderían a objetos activos, mientras que las IP corresponderían a objetos pasivos.

### Modelo de actor

PBF considera al actor de Carl Hewitt como un proceso asincrónico con 2 puertos: uno para mensajes de entrada y otro para señales de control. La señal de control es emitida por el propio actor después de cada ronda de ejecución. El propósito de esta señal es evitar la ejecución paralela del cuerpo del actor y permitir el acceso a los campos del objeto del actor sin sincronización.

