paradigma de programación

40
Paradigma de programación A lo largo de la historia, el término “paradigma” fue objeto de muchas interpretaciones. En su origen griego, significaba “modelo”, “ejemplo” o “patrón”. Sobre este punto de partida, podemos hablar de un paradigma como un conjunto de creencias, prácticas y conocimientos que guían el desarrollo de una disciplina durante un período de tiempo. En diversas ramas de la ciencia, un conjunto de ideas en vigencia puede ser reemplazado drásticamente por otro que entre en conflicto con él y se demuestre más acertado. La programación tiene sus propios paradigmas, pero el término “paradigma de programación” no necesariamente representa un modelo único que deba ser respetado hasta que aparezca otro mejor. De hecho, actualmente muchos paradigmas coexisten en armonía. Un paradigma de programación es un estilo de desarrollo de programas. Es decir, un modelo para resolver problemas computacionales. Los lenguajes de programación, necesariamente, se encuadran en uno o varios paradigmas a la vez a partir del tipo de órdenes que permiten implementar, algo que tiene una relación directa con su sintaxis. ¿Cuáles son los principales paradigmas de programación? Imperativo. Los programas se componen de un conjunto de sentencias que cambian su estado. Son secuencias de comandos que ordenan acciones a la computadora. Declarativo. Opuesto al imperativo. Los programas describen los resultados esperados sin listar explícitamente los pasos a llevar a cabo para alcanzarlos. Lógico. El problema se modela con enunciados de lógica de primer orden. Funcional. Los programas se componen de funciones, es decir, implementaciones de comportamiento que reciben un conjunto de datos de entrada y devuelven un valor de salida. Orientado a objetos. El comportamiento del programa es llevado a cabo por objetos, entidades que representan elementos del problema a resolver y tienen atributos y comportamiento. Otros son de aparición relativamente reciente y no forman parte del grupo principal: Dirigido por eventos. El flujo del programa está determinado por sucesos externos (por ejemplo, una acción del usuario). Orientado a aspectos. Apunta a dividir el programa en módulos independientes, cada uno con un comportamiento bien definido.

Upload: juan-garcia

Post on 15-Jan-2016

11 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Paradigma de Programación

Paradigma de programación

A lo largo de la historia, el término “paradigma” fue objeto de muchas interpretaciones. En su origen griego, significaba “modelo”, “ejemplo” o “patrón”. Sobre este punto de partida, podemos hablar de un paradigma como un conjunto de creencias, prácticas y conocimientos que guían el desarrollo de una disciplina durante un período de tiempo. En diversas ramas de la ciencia, un conjunto de ideas en vigencia puede ser reemplazado drásticamente por otro que entre en conflicto con él y se demuestre más acertado. La programación tiene sus propios paradigmas, pero  el término “paradigma de programación” no necesariamente representa un modelo único que deba ser respetado hasta que aparezca otro mejor. De hecho, actualmente muchos paradigmas coexisten en armonía.

Un paradigma de programación es un estilo de desarrollo de programas. Es decir, un modelo para resolver problemas computacionales. Los lenguajes de programación, necesariamente, se encuadran en uno o varios paradigmas a la vez a partir del tipo de órdenes que permiten implementar, algo que tiene una relación directa con su sintaxis.

¿Cuáles son los principales paradigmas de programación?

Imperativo. Los programas se componen de un conjunto de sentencias que cambian su estado. Son secuencias de comandos que ordenan acciones a la computadora.

Declarativo. Opuesto al imperativo. Los programas describen los resultados esperados sin listar explícitamente los pasos a llevar a cabo para alcanzarlos.

Lógico. El problema se modela con enunciados de lógica de primer orden. Funcional. Los programas se componen de funciones, es decir, implementaciones

de comportamiento que reciben un conjunto de datos de entrada y devuelven un valor de salida.

Orientado a objetos. El comportamiento del programa es llevado a cabo por objetos, entidades que representan elementos del problema a resolver y tienen atributos y comportamiento.

Otros son de aparición relativamente reciente y no forman parte del grupo principal:

Dirigido por eventos. El flujo del programa está determinado por sucesos externos (por ejemplo, una acción del usuario).

Orientado a aspectos. Apunta a dividir el programa en módulos independientes, cada uno con un comportamiento bien definido.

Cada paradigma es ideal para la resolución de un conjunto de problemas particular, por lo que no puede decirse que uno sea necesariamente mejor que otro.

Page 2: Paradigma de Programación

Paradigma Funcional

IntroducciónEl objetivo del paradigma funcional es conseguir lenguajes expresivos y matemáticamente elegantes, en los que no sea necesario bajar al nivel de la máquina para describir el proceso llevado a cabo por el programa, y evitando el concepto de estado del cómputo. La secuencia de computaciones llevadas a cabo por el programa se regiría única y exclusivamente por la reescritura de definiciones más amplias a otras cada vez más concretas y definidas, usando lo que se denominan definiciones dirigidas.Todo esto con el objetivo de familiarizar a los estudiantes con un lenguaje elegante en el cual se pueda manejar más fácilmente y así los programas sean menos extensos y complejos.Otro de los objetivos primordiales de dicho paradigma es buscar satisfacer las necesidades del usuario con respecto a operaciones matemáticas y convertirse en un lenguaje más expresivo.

HistoriaSus orígenes provienen del Cálculo Lambda (o λ-cαlculo), una teoría matemática elaborada por Alonso Church como apoyo a sus estudios sobre Computabilidad. Un lenguaje funcional es, a grandes rasgos, un azúcar sintáctico del Cálculo Lambda.Cálculo LambdaLos orígenes teóricos del modelo funcional se remontan a la década del 30, mas precisamente al año 1934, cuando Alonso Church introdujo un modelo matemático de computación llamado lambda calculo.A pesar de que en esta época las computadoras aun no existían el lambda cálculo se puede considerar como el primer lenguaje funcional de la historia y sus fundamentos fueron la base de toda la teoría de la programación funcional y de los lenguajes funcionales desarrollados posteriormente. Se puede decir que los lenguajes funcionales modernos son versiones de lambda cálculo con numerosas ayudas sintácticas.Aunque cuando aparece el cálculo lambda, aún no existían las computadoras, resulta ser una herramienta simple que se adelanta a su época, que abarca dos operaciones:a. Definir alguna(s) función(es) de un solo argumento y con un cuerpo específico,

denotado por la siguiente terminología:b. lx.B, en donde:

i. x: Define el parámetro o argumento formal.ii.iii. B: Representa el cuerpo de la función.iv. Es decir f(x) = B.

a. REDUCCION:Consiste en aplicar alguna de las funciones creadas, sobre un argumento real (A); resultado de sustituir las ocurrencias del argumento formal (x), que aparezcan en el cuerpo (B) de la función, con el argumento (A),

es decir: (lx.B)Ejemplo:(lx.(x+5))3, indica que en la expresión x + 5, debemos sustituir el valor de x por 3.

Page 3: Paradigma de Programación

Cuando ya no es posible reducir una función, se dice que ésta se encuentra en su estado normal, o sea hemos encontrado el valor de la función, que dependerá únicamente de los argumentos y siempre tendrá la consistencia de regresar el mismo valor para los mismos argumentos.Lo anterior es la transferencia referencial y al no contar con variables globales, permiten al sistema la ejecución de procesos en forma paralela para incrementar su eficiencia.Sobre estos simples conceptos está basada la programación funcional, aunque existen otros, usados para identificarla y aumentan su potencial en el desarrollo de aplicaciones.CaracterísticasLos programas escritos en un lenguaje funcional están constituidos únicamente por definiciones de funciones, entendiendo éstas no como subprogramas clásicos de un lenguaje imperativo, sino como funciones puramente matemáticas, en las que se verifican ciertas propiedades como la transparencia referencial (el significado de una expresión depende únicamente del significado de sus subexpresiones), y por tanto, la carencia total de efectos laterales.Otras características propias de estos lenguajes son la no existencia de asignaciones de variables y la falta de construcciones estructuradas como la secuencia o la iteración (lo que obliga en la práctica a que todas las repeticiones de instrucciones se lleven a cabo por medio de funciones recursivas).Existen dos grandes categorías de lenguajes funcionales: los funcionales puros y los híbridos. La diferencia entre ambos estriba en que los lenguajes funcionales híbridos son menos dogmáticos que los puros, al admitir conceptos tomados de los lenguajes procedimentales, como las secuencias de instrucciones o la asignación de variables.En contraste, los lenguajes funcionales puros tienen una mayor potencia expresiva, conservando a la vez su transparencia referencial, algo que no se cumple siempre con un lenguaje funcional híbrido.Entre los lenguajes funcionales puros, cabe destacar a Haskell y Miranda. Los lenguajes funcionales híbridos más conocidos son Lisp, Scheme, Ocaml y Standard ML (estos dos últimos, descendientes del lenguaje ML).La programación funcional, es un modelo basado en la evaluación de funciones matemáticas, entendidas como mecanismos para aplicar ciertas operaciones sobre algunos valores o argumentos, para obtener un resultado o valor de la función para tales argumentos.Sin embargo, tanto argumentos como resultado de una función, pueden ser otra función, o incluso la misma, tal como una forma de recursividad, que constituye una poderosa herramienta de la programación funcionalLenguajes FuncionalesLos matemáticos desde hace un buen tiempo están resolviendo problemas usando el concepto de función. Una función convierte ciertos datos en resultados. Si supiéramos cómo evaluar una función, usando la computadora, podríamos resolver automáticamente muchos problemas.Así pensaron algunos matemáticos, que no le tenían miedo a la máquina, e inventaron los lenguajes de programación funcionales. Además, aprovecharon la posibilidad que tienen las funciones para manipular datos simbólicos, y no solamente numéricos, y la propiedad de las funciones que les permite componer, creando de esta manera, la oportunidad para resolver problemas complejos a partir de las soluciones a otros más sencillos.También se incluyó la posibilidad de definir funciones recursivamente.Un lenguaje funcional ofrece conceptos que son muy entendibles y relativamente fáciles de manejar para todos los que no se durmieron en las clases de matemáticas. El lenguaje funcional más antiguo, y seguramente el más popular hasta la fecha, es LISP, diseñado por McCarthy [1] en la segunda mitad de los años 50. Su área de aplicación

Page 4: Paradigma de Programación

es principalmente laInteligencia Artificial. En la década de los 80 hubo una nueva ola de interés por los lenguajes funcionales, añadiendo la tipificación y algunos conceptos modernos de modularización y polimorfismo, como es el caso del lenguaje ML.Programar en un lenguaje funcional significa construir funciones a partir de las ya existentes. Por lo tanto es importante conocer y comprender bien las funciones que conforman la base del lenguaje, así como las que ya fueron definidas previamente. De esta manera se pueden ir construyendo aplicaciones cada vez más complejas.La desventaja de este modelo es que resulta bastante alejado del modelo de la máquina de von Neumann y, por lo tanto, la eficiencia de ejecución de los intérpretes de lenguajes funcionales no es comparable con la ejecución de los programas imperativos precompilados. Para remediar la deficiencia, se está buscando utilizar arquitecturas paralelas que mejoren eldesempeño de los programas funcionales, sin que hasta la fecha estos intentos tengan un impacto real importante.Los dos mecanismos básicos presentados anteriormente se corresponden con los conceptos de abstracción funcional y aplicación de función; si le agregamos un conjunto de identificadores para representar variables se obtiene lo mínimo necesario para tener un lenguaje de programación funcional. Lambda calculo tiene el mismo poder computacional que cualquier lenguaje imperativo tradicional.Las ventajas de tener un lenguaje tan simple son:

Permite definiciones simples. Facilita el estudio de aspectos computacionales. Su carácter formal facilita la demostración de propiedades.

Aplicaciones Compilación de lenguajes funcionales. Especificar semántica a lenguajes imperativos. Formalismo para definir otras teorías.

Programación funcional:La crítica de John Backus a los "lenguajes convencionales de von Neumann" llamó la atención hacia la programación funcional (hacia 1978). El estilo de programación adoptado por Backus fue denominado FP (Functional Programming).El componente básico de los lenguajes funcionales es la noción de función y su estructura de control esencial la aplicación de una función. Entre los lenguajes funcionales se encuentran ISWIM, ML, LISP y todos sus derivados, como Scheme. Las características fundamentales de los lenguajes funcionales de programación son, según [SET92]:1. El valor de una expresión depende sólo de los valores de sus subexpresiones, si las tiene. La programación funcional pura es una programación sin asignaciones. En realidad, la mayoría de los lenguajes funcionales son impuros, ya que permiten asignaciones. Sin embargo, su estilo de programación es diferente al de los lenguajes de programación imperativa.2. Almacenamiento implícito. El programador no debe preocuparse en manejar el almacenamiento de datos. Una consecuencia de esto es que la implementación del lenguaje debe realizar una "recolección de basura" para recuperar la memoria que se ha usado y no se volverá a utilizar.3. Las funciones son valores de primera clase. Una función puede ser el valor de una expresión, pasarse como argumento o colocarse en una estructura de datos. Esto permite potentes operaciones.Al decir que un lenguaje de programación es funcional, se suele hacer referencia al paradigma de programación funcional.

Page 5: Paradigma de Programación

En términos general, al desarrollar software en el paradigma de programación, en contraste con el paradigma de programación imperativo, se tiende a enfatizar más la evaluación de expresiones que la ejecución secuencial de comandos.En el fondo, para ejecutar programas los computadores ejecutan, de manera secuencial, instrucciones de código de máquina. De cierta forma, ésto hace que los lenguajes imperativos sean más cercanos a la manera en que funcionan los computadores, al forzar a los programadores a formular sus algoritmos como una serie de pasos que se realizarán de manera secuencial.Los lenguajes funcionales, por otro lado, son más cercanos a la manera en que funciona la mente humana, pues tienden a permitirle a los programadores describir sus algoritmos como expresiones que serán evaluadas.Hay varias consecuencias del énfasis en la evaluación de expresiones que son comunes a la mayoría de lenguajes funcionales:

Los procedimientos son ciudadanos de primera categoría Los procedimientos y funciones son objetos visibles que pueden ser almacenados dentro de estructuras complejas, pasados como argumentos, construidos en tiempo de ejecución y manipulados al igual que otros tipos como los números y las cadenas.

Manejo automático de memoria El programador no necesita llevar un registro manual de la memoria utilizada por cada objeto y liberarla; la implementación del lenguaje se encarga de eso de manera automática.

Los lenguajes funcionales han permanecido en uso por mucho tiempo y han mostrado todo el poder de su gran nivel de expresividad. Al permitir la creación de procedimientos en tiempo de ejecución, permiten un gran nivel de modularidad que, de acuerdo con sus proponentes, difícilmente puede alcanzarse en otros paradigmas de programación.Dentro de los lenguajes funcionales se encuentran Lisp, Haskell, CaML y muchos otros.El lenguaje provee algunas funciones basicas, que son primitivas para construir funciones más complejas. Se definen algunas estructuras para representar datos en los parámetros y resultados de las funciones. Este paradigma normalmente se implementa mediante interpretadores, pero tambien se pueden compilar.FuncionesLos primeros ordenadores se construyeron en los años cuarenta. Pronto se almacenaron los programas en la memoria del ordenador, haciendo que los primeros lenguajes de programación hicieran su entrada. En aquel tiempo el uso de un ordenador era muy costoso y era lógico que el lenguaje de programación guardara mucha relación con la arquitectura del ordenador.Un ordenador consta de una unidad de control y una memoria. Por eso un programa consistía en instrucciones para cambiar el contenido de la memoria. La unidad de control se encargaba de ejecutarlas. De esta manera se creo el estilo de programación imperativa. Los lenguajes de programación imperativa como Pascal y C se caracterizan por la existencia de asignaciones ejecutadas consecutivamente.Antes de la existencia de los ordenadores se inventaron métodos para resolver problemas. Por tanto, no existía la necesidad de hablar en términos de una memoria que cambie por instrucciones en un programa.En la matemática de los últimos cuatrocientos años son muy importantes las funciones. Estas establecen la relación entre los parámetros (la ‘entrada’) y el resultado (la ‘salida’) de procesos definidos. Con cada computación, el resultado depende de una u otra forma de los parámetros. Por esa razón, una función es una buena manera de especificar una computación. Esta es la base del estilo de programación funcional. Un ‘programa’ consiste en la definición de una o mas funciones.Para la ejecución de un programa, se dan parámetros a una función y el ordenador tiene que calcular el resultado. Con este tipo de computación existe libertad en la

Page 6: Paradigma de Programación

manera de ejecución. ¿Por qué tendría que describirse en qué orden deben ejecutarse las computaciones parciales?Con el tiempo, al bajar los precios de los ordenadores y al subir los precios de los programadores, llega a ser más importante describir las computaciones en un lenguaje que esté más cerca del mundo del hombre, que cerca del ordenador. Los lenguajes funcionales se unen a la tradición matemática y no están muy influidos por la arquitectura concreta del ordenador.SchemeScheme es un lenguaje funcional, derivado de LISP. Scheme es un lenguaje compacto con un alto grado de abstracción, por lo cual resulta adecuado para cursos introductorios de computación, donde el énfasis está en la metodología de resolución de problemas. Scheme permite resolver problemas complejos con programas cortos y elegantes. De este modo, el lenguaje se convierte en un aliado para resolver problemas, no un problema más que resolver.HistoriaEl desarrollo de los lenguajes funcionales ha sido influenciado a lo largo de la historia por muchas fuentes, pero ninguna tan grande ni fundamental como el trabajo de Alonzo Church en el Cálculo Lambda.El Cálculo Lambda fue asimismo el fundamento en que se basó el desarrollo del Lisp, pero el propio John McCarthy ha negado ese hecho3. El impacto del Cálculo Lambda en el desarrollo inicial del Lisp fue mínimo y no ha sido sino hasta recientemente que el popular lenguaje ha comenzado a evolucionar de acuerdo con los ideales del mismo. Por otra parte, Lisp ha tenido una gran influencia en el desarrollo de los lenguajes funcionales que le siguieron.La motivación original de McCarthy para desarrollar Lisp fue el deseo de contar con un lenguaje para procesamiento de listas algebraicas que pudiera usarse para llevar a cabo investigación en inteligencia artificial.Uno de los primeros intentos por desarrollar un lenguaje de tal naturaleza fue realizado por McCarthy, con el llamado FLPL (FORTRAN - compiled List Processing Language), que se implementó en el FORTRAN con que contaba la IBM 704 en 1958. Durante los años subsiguientes McCarthy diseñó, refinó e implementó Lisp (List Processor), en parte porque FLPL no contaba con recursividad ni con condicionales dentro de las expresiones.Lisp no es sólo uno de los lenguajes más viejos que existen, sino también el primero en proporcionar recursividad, funciones como ciudadanos de primera clase, recolección de basura y una definición formal del lenguaje (escrita asimismo en Lisp). Las diversas implementaciones de Lisp desarrolladas a través de los años han sido también pioneras en cuanto al uso de ambientes integrados de programación, los cuales combinan editores, intérpretes y depuradores.Las primeras implementaciones de Lisp tuvieron, sin embargo, algunos problemas que las hicieron perder popularidad, como por ejemplo su tremenda lentitud para efectuar cálculos numéricos, su sintaxis basada por completo en paréntesis que suele causar gran confusión entre los usuarios novatos y su carencia de tipos que hace difícil la detección de errores y el desarrollo de compiladores.Además, problemas adicionales tales como la carencia de verdaderas funciones como ciudadanos de primera clase y el uso de reglas de ámbito dinámicas, bajo las cuales el valor de una variable libre se toma del ambiente de activación, hicieron que Lisp se mantuviera durante un buen tiempo como un lenguaje restringido a los laboratorios de investigación, lejos del alcance de un número significativo de usuarios. El mismo McCarthy afirmó que su lenguaje no era apropiado "para los programadores novatos o los no programadores", pues se requería una cierta cantidad de "conocimientos sofisticados para apreciar y usar el lenguaje efectivamente".A partir de 1962, el desarrollo del Lisp divergió en un gran número de dialectos, entre los que destacan MacLisp y ZetaLisp en el Instituto Tecnológico de Massachusetts

Page 7: Paradigma de Programación

(MIT), Franz Lisp en la Universidad de California en Berkeley, ICI-Lisp en la Universidad de Stanford, y el InterLisp, un producto comercial desarrollado por Bolt, Boranek y Newman (en un laboratoriode investigación privado de los Estados Unidos).En el otoño de 1975 Gerald Jay Sussman y Guy Lewis Steele Jr. se encontraban estudiando la teoría de los actores como un modelo de computación desarrollada por Carl Hewitt en MIT.El modelo de Hewitt estaba orientado a objetos y con una fuerte influencia del Smalltalk. De acuerdo con él, cada objeto era una entidad computacionalmente activa capaz de recibir y de reaccionar a los mensajes. A estos objetos y los mensajes que intercambiaban, Hewitt los llamó actores. Dado que Sussman y Steele afrontaban dificultades para entender algunos aspectos teóricos y prácticos del trabajo de Hewitt, decidieron construir un pequeño intérprete de este lenguaje usando MacLisp, a fin de poder experimentar con él. Como Sussman había estado estudiando Algol en aquella época, le sugirió a Steele comenzar con un dialecto de Lisp que tuviera reglas de ámbito estático (i.e., el valor de una variable libre se toma de su ambiente de definición).Esta decisión les permitió crear actores con la misma facilidad con que se crean las funciones en Lisp (y usando casi los mismos mecanismos). El paso de mensajes se podría entonces expresar sintácticamente en la misma forma en que se invoca una función. La única diferencia entre un actor y una función era que una función regresaba un valor y un actor no regresaba nada, sino que más bien invocaba una continuación, o sea, otro actor que sabía de su existencia.Sussman y Steele se sintieron tan satisfechos con su mini-intérprete que decidieron llamarlo "Schemer", pensando que con el tiempo se convertiría en otro lenguaje que se pudiera utilizar en inteligencia artificial, tal y como PLANNER, el lenguaje desarrollado por Hewitt. Sin embargo, el sistema operativo ITS limitaba los nombres a 6 letras, por lo que el apelativo del intérprete hubo de ser truncado a "Scheme", que es como se le conoce hoy en día.Persuadidos de que su intérprete parecía capturar muchas de las ideas que circulaban entonces sobre lenguajes de programación, Sussman y Steele decidieron publicar la definición de Scheme en la forma de un memo del laboratorio de inteligencia artificial del MIT. Esta primera definición del lenguaje era sumamente austera, con un mínimo de primitivas (una por concepto), pero marcó el inició de lo que se convertiría en un lenguaje de programación sumamente popular en las esferas académicas.En 1976, Sussman y Steele publicaron dos artículos en los que se hablaba sobre semántica de los lenguajes de programación usando Scheme: "Lambda: The Ultimate Imperative" y "Lambda: The Ultimate Declarative", en los cuales los autores enfatizarían el hecho de que Scheme podía soportar eficientemente los principales paradigmas de programación actuales (i.e., imperativo, funcional y orientado a objetos). Esta última publicación se convirtió de hecho en la propuesta de tesis de maestría de Steele, que culminó en el desarrollo de un compilador de Scheme llamado RABBIT.Históricamente, Scheme contribuyó a estrechar la brecha entre los teóricos (que estudiaban el Cálculo Lambda y el modelo de actores) y los prácticos (implantadores y usuarios de Lisp) en el área de lenguajes de programación. Además, Scheme hizo la semántica denotacional mucho más accesible a los programadores y proporcionó una plataforma operacional que permitiera a los teóricos realizar sus experimentos. Por su reducido tamaño, no había necesidad de tener una versión centralizada del lenguaje que tuviera que soportar un gran número de plataformas, como sucedía con Lisp.De tal forma, brotaron por todas partes implementaciones y dialectos de Scheme hacia inicios de los 80. Un ejemplo es el Scheme 311, desarrollado en la Universidad de Indiana varios años antes de que alguien hiciera un intento por producir una implementación aislada del lenguaje (i.e., que no tuviera que montarse sobre Lisp).Conclusión

Page 8: Paradigma de Programación

Hoy en día Scheme es un lenguaje simple, pero poderoso; pequeño, pero flexible. Su naturaleza lo hace ideal para la enseñanza, incluso como primer lenguaje de programación, por su notable facilidad para incorporar diversos paradigmas con sólo un puñado de primitivas.Como lenguaje imperativo, conserva la pureza y elegancia que le proporcionan la recursividad y las funciones de orden superior, superando así a muchos de los lenguajes que hoy gozan de gran popularidad, tales como Pascal y C. Como lenguaje orientado a objetos, hace alarde de un sistema de paso de mensajes y de manipulación de objetos equiparable únicamente al de Smalltalk. Su mecanismo de continuaciones proporciona procedimientos de escape como ciudadanos de primer orden, condenando a la obsolescencia a los mecanismos similares con que cuentan otros lenguajes.El uso de streams permite implementar la evaluación concisa, convirtiendo al lenguaje en una herramienta idónea para cursos avanzados de lenguajes de programación. Una de sus pocas desventajas estriba en su carencia de tipos, aunque existen versiones de Scheme que incorporan un sistema similar al de ML (por ejemplo, Scheme 48).Desde una perspectiva más pragmática, podemos decir que su sintaxis es extremadamente simple, lo que permite que el lenguaje pueda dominarse fácilmente en sólo 6 meses. Los estudiantes suelen preferirlo, sobre todo, cuando se trata del primer lenguaje de programación que aprenden, y los instructores lo elogian porque facilita la enseñanza de ideas de abstracción y diseño de algoritmos, tan útiles para formar buenos programadores.Con la sabia filosofía de proporcionar sólo una primitiva que haga lo que queremos en vez de varias, como en otros lenguajes, Scheme se erige en la cima de los lenguajes preferidos por las nuevas generaciones, ya no sólo como una mera curiosidad científica, como se vió a Lisp en otra época, sino como una herramienta efectiva que sirve para aprender a programar y a resolver problemas del mundo real.

Paradigma funcional

Diagrama del funcionamiento de una subrutina.

En ciencias de la computación, la programación funcional es un paradigma de programación declarativa basado en la utilización de funciones aritméticas que no maneja datos mutables o de estado. Enfatiza la aplicación de funciones, en contraste con el estilo de programación imperativa, que enfatiza los cambios de estado. La programación funcional tiene sus raíces en el cálculo lambda, un sistema formal desarrollado en los años 1930 para investigar la definición de función, la aplicación de

Page 9: Paradigma de Programación

las funciones y la recursión. Muchos lenguajes de programación funcionales pueden ser vistos como elaboraciones del cálculo lambda.

En la práctica, la diferencia entre una función matemática y la noción de una "función" utilizada en la programación imperativa es que las funciones imperativas pueden tener efectos secundarios, al cambiar el valor de cálculos realizados previamente. Por esta razón carecen de transparencia referencial, es decir, la misma expresión sintáctica puede resultar en valores diferentes en diferentes momentos dependiendo del estado del programa siendo ejecutado. Con código funcional, en contraste, el valor generado por una función depende exclusivamente de los argumentos alimentados a la función. Al eliminar los efectos secundarios se puede entender y predecir el comportamiento de un programa mucho más fácilmente, y esta es una de las principales motivaciones para utilizar la programación funcional.

Los lenguajes de programación funcional, especialmente los que son puramente funcionales, han sido enfatizados en el ambiente académico principalmente y no tanto en el desarrollo de software comercial. Sin embargo, lenguajes de programación importantes tales como Scheme, Erlang, Rust, Objective Caml y Haskell, han sido utilizados en aplicaciones comerciales e industriales por muchas organizaciones. La programación funcional también es utilizada en la industria a través de lenguajes de dominio específico como R (estadística), Mathematica (matemáticas simbólicas), J y K (análisis financiero), F# en Microsoft.NET y XSLT (XML). Lenguajes de uso específico usados comúnmente como SQL y Lex/Yacc, utilizan algunos elementos de programación funcional, especialmente al procesar valores mutables. Las hojas de cálculo también pueden ser consideradas lenguajes de programación funcional.

La programación funcional también puede ser desarrollada en lenguajes que no están diseñados específicamente para la programación funcional. En el caso de Perl, por ejemplo, que es un lenguaje de programación imperativo, existe un libro que describe como aplicar conceptos de programación funcional. JavaScript, uno de los lenguajes más ampliamente utilizados en la actualidad, también incorpora capacidades de programación funcional. Python también incorpora particularidades de los lenguajes funcionales como listas de comprensión y funciones de tratamiento de listas como matemática de conjuntos. Java en su versión 8, esta incorporando la programación funcional, así como el uso de las expresiones lambda.

Utilidad

El objetivo es conseguir lenguajes expresivos y matemáticamente elegantes, en los que no sea necesario bajar al nivel de la máquina para describir el proceso llevado a cabo por el programa, y evitar el concepto de estado del cómputo. La secuencia de computaciones llevadas a cabo por el programa se rige única y exclusivamente por la reescritura de definiciones más amplias a otras cada vez más concretas y definidas, usando lo que se denominan "definiciones dirigidas".

Características

Los programas escritos en un lenguaje funcional están constituidos únicamente por definiciones de funciones, entendiendo éstas no como subprogramas clásicos de un lenguaje imperativo, sino como funciones puramente matemáticas, en las que se

Page 10: Paradigma de Programación

verifican ciertas propiedades como la transparencia referencial (el significado de una expresión depende únicamente del significado de sus subexpresiones), y por tanto, la carencia total de efectos colaterales.

Otras características propias de estos lenguajes son la no existencia de asignaciones de variables y la falta de construcciones estructuradas como la secuencia o la iteración (lo que obliga en la práctica a que todas las repeticiones de instrucciones se lleven a cabo por medio de funciones recursivas).

Existen dos grandes categorías de lenguajes funcionales: los funcionales puros y los híbridos. La diferencia entre ambos estriba en que los lenguajes funcionales híbridos son menos dogmáticos que los puros, al admitir conceptos tomados de los lenguajes imperativos, como las secuencias de instrucciones o la asignación de variables. En contraste, los lenguajes funcionales puros tienen una mayor potencia expresiva, conservando a la vez su transparencia referencial, algo que no se cumple siempre con un lenguaje funcional híbrido.

Funciones de primera clase y de orden superior

Funciones de orden superior son funciones que pueden tomar otras funciones como argumentos o devolverlos como resultados. En cálculo , un ejemplo de una función de orden superior es el operador diferencial d / dx , que devuelve la derivada de una función f .

Las funciones de orden superior están estrechamente relacionadas con las funciones de primera clase en las cuales las funciones de orden superior y las funciones de primera clase pueden recibir como argumentos y resultados otras funciones. La distinción entre los dos es sutil: "de orden superior", describe un concepto matemático de funciones que operan sobre otras funciones, mientras que la "primera clase" es un término informático que describe las entidades del lenguaje de programación que no tienen ninguna restricción de su utilización (por lo tanto funciones de primera clase pueden aparecer en cualquier parte del programa que otras entidades de primer nivel como los números pueden, incluidos como argumentos a otras funciones y como sus valores de retorno).

Las funciones de orden superior permiten la aplicación parcial, una técnica en la que se aplica una función a sus argumentos uno a la vez, con cada aplicación devolver una nueva función que acepta el siguiente argumento. Esto le permite a uno expresar, por ejemplo, la función sucesor como el operador de suma aplicada parcialmente al número natural uno.

Funciones puras

Las funciones puramente funcionales (o expresiones) no tienen efectos secundarios (memoria o E/S). Esto significa que las funciones puras tienen varias propiedades útiles, muchas de las cuales pueden ser utilizados para optimizar el código:

Si no se utiliza el resultado de una expresión pura, se puede eliminar sin afectar a otras expresiones.

Page 11: Paradigma de Programación

Si una función pura se llama con parámetros que no causan efectos secundarios, el resultado es constante con respecto a la lista de parámetros (a veces llamada transparencia referencial), es decir, si la función pura se llama de nuevo con los mismos parámetros, el mismo resultado será devuelto (esto puede habilitar las optimizaciones de almacenamiento en caché).

Si no hay una dependencia de datos entre dos expresiones puras, entonces su orden puede ser invertido, o pueden llevarse a cabo en paralelo y que no pueda interferir con los otros.

Si el lenguaje no permite efectos secundarios, entonces cualquier estrategia de evaluación se puede utilizar, lo que da la libertad al compilador para reordenar o combinar la evaluación de expresiones en un programa (por ejemplo, usando la poda).

La mayoría de los compiladores de lenguajes imperativos detectan funciones puras automáticamente y realizan la eliminación de subexpresiones comunes. Sin embargo no siempre es posible detectarlo en bibliotecas pre-compiladas, porque por norma general no dan esta información. Esto provoca que no se puedan realizar optimizaciones que podrían aplicar a dichas funciones externas. Algunos compiladores, como gcc, sí que añaden palabras claves adicionales para que el programador marque explícitamente como puras aquellas funciones externas que proceda, de modo que se le apliquen las optimizaciones pertinentes. Fortran 95 también permite que declarar funciones "puras".

Recursividad

Iterar en los lenguajes funcionales es normalmente llevado a cabo mediante recursividad. Las funciones recursivas se invocan a sí mismas, permitiendo que una operación se realice una y otra vez hasta alcanzar el caso base. Aunque algunas recursividades requieren el mantenimiento de una pila, la recursividad mediante una cola puede ser reconocida y optimizada mediante un compilador dentro del mismo código utilizado, para implementar las iteraciones en un lenguaje imperativo. El estándar del esquema del lenguaje requiere implementaciones para conocer y optimizar la recursividad mediante una cola. La optimización de la recursividad mediante una cola puede ser implementada transformando el programa a un estilo de pase de continuidad durante la compilación, entre otros enfoques.

Los patrones comunes de recursividad puede ser factorizados usando funciones comunes más grandes, con “catamorfismos” y “anamorfismos” (pliegues y despliegues), siendo estos los ejemplos más evidentes. Tal y como las mayores funciones más comunes tienen un rol análogo para construir estructuras de control se tienen los iteradores en los lenguajes imperativos.

La mayoría de los lenguajes de programación funcional de propósito general permiten la recursividad sin restricciones y superan el test de Turing, lo que hace que el programa que se interrumpe no pueda tomar un decisión, lo que puede causar una falta de solidez en el razonamiento ecuacional y generalmente requiere introducir inconsistencia dentro de la lógica expresada por los tipos del sistema del lenguaje. Algunos lenguajes de propósito especial como Coq permiten tan sólo recursividad bien

Page 12: Paradigma de Programación

fundamentada y tienen una normalización fuerte(cálculos no finalizados pueden ser expresados tan sólo con flujos de valores infinitos llamados codata) En consecuencia, estos lenguajes fallan el test de Turing y declarar funciones ciertas en ellos es imposible, pero pueden declarar una amplia clase de cálculos interesantes mientras evitan los problemas producidos por la recursividad sin restricciones. La programación funcional limitada a la recursividad bien construida con unas cuantas restricciones más se llama programación funcional total.

Evaluación estricta frente a la no estricta

Los lenguajes funcionales pueden ser clasificados por el hecho de usar evaluación estricta(eager) o no estricta(lazy), conceptos que hacen referencia a cómo los argumentos de las funciones son procesados cuando una expresión está siendo evaluada. La diferencia técnica está en la notación semántica de las expresiones que contienen cálculos fallidos o divergentes. Bajo la evaluación estricta, la evaluación de cualquier término que contenga un sub-término fallido hará que este sea de por sí fallido.

Por ejemplo, la expresión:

print length([2+1, 3*2, 1/0, 5-4])

fallará bajo evaluación estricta por la división por cero en el tercer elemento de la lista. Utilizando evaluación no estricta, el tamaño de la función devolverá un valor de 4( por ejemplo el número de elementos de la lista) ya que evaluar esto no afectará al estar evaluando los que componen la lista. En resumen, la evaluación estricta evalúa por completo los argumentos a menos que sus valores requieran evaluar la propia función que se llama a sí misma.

La implementación de la estrategia común para evaluación no estricta en los lenguajes funcionales es la de reducción mediante un grafo. La evaluación no estricta es utilizada por defecto en multitud de lenguajes funcionales puros, incluidos Miranda, Clean y Haskell.

Hughes (1984) defendía la evaluación no estricta como un mecanismo para mejorar la modularidad de los programas a través de la separación de tareas, a partir de la implementación de productores y consumidores de flujos de datos de forma fácil e independiente. Launchbury (1993) describe algunas dificultades que tenía la evaluación no estricta, particularmente al analizar los requisitos de almacenamiento de los programas, y propone una semántica operacional para ayudar durante el análisis. Harper (2009) propone incluir ambas técnicas (evaluación estricta y no estricta) en el mismo lenguaje, utilizando los tipos del sistema del lenguaje para distinguirlas.

Tipo de sistemas

Especialmente desde el desarrollo de tipos de inferencia de Hindley - Milner en la década de 1970, los lenguajes de programación funcionales han tendido a utilizar el cálculo con tipo lambda, en comparación con el cálculo lambda sin tipo utilizado en Lisp y sus variantes (tales como el lenguaje scheme). El uso de tipos de datos algebraicos y la coincidencia de patrones hace que la manipulación de estructuras de datos complejas convenientes y expresivos, la presencia de comprobaciones estrictas

Page 13: Paradigma de Programación

de tipos en tiempo de compilación hace que los programas sean más fiable, mientras que la inferencia de tipos libera al programador de la necesidad de declarar manualmente los tipos para el compilador.

Algunos lenguajes funcionales orientados a la investigación, tales como Coq, Agda, Cayenne y Epigram se basan en la teoría de tipo intuicionista, que permite a los tipos a depender de los términos. Estos tipos se denominan tipos dependientes. Estos sistemas de tipo no tienen un tipo decidible inferencia y son difíciles de entender y programar con plantillas de citación. Pero tipos dependientes pueden expresar proposiciones arbitrarias en la lógica de predicados.

A través del isomorfismo de Curry-Howard, entonces, mecanografiados a programas en estas lenguas se convierten en una forma de escribir las pruebas matemáticas formales de las que un compilador puede generar código de certificado. Si bien estas lenguas son principalmente de interés en la investigación académica (incluyendo las matemáticas formalizadas), han comenzado a ser utilizado en la ingeniería también. Compcert es un compilador para un subconjunto del lenguaje de programación C que se escribe en Coq y verificó formalmente. Una forma limitada de tipos dependientes llamados tipos de datos algebraicos generalizados (GADTs) puede ser implementado de una manera que ofrece algunos de los beneficios de la programación dependiente escrito, evitando la mayor parte de su inconveniencia. GADTs están disponibles en elGlasgow Haskell Compiler, en OCaml (desde la versión 4.00) y en Scala y se han propuesto como adiciones a otros idiomas, incluyendo Java y C#.

La programación funcional en lenguajes no funcionalesEs posible utilizar un estilo de programación funcional en lenguajes que tradicionalmente no se consideran lenguajes funcionales. Por ejemplo, tanto D y Fortran95 se apoyan explícitamente en funciones puras. Funciones de primera clase, se han añadido lentamente a los lenguajes principales. Por ejemplo, a principios de 1994, el apoyo a lambda, filtro, mapa, y reducir esta en Python. Luego, durante el desarrollo de Python 3000, Guido van Rossum pidió la eliminación de estas características. Sin embargo, más tarde cambió de opinión, y sólo la reducción fue eliminado, a pesar de que sigue siendo accesible a través de los módulos de biblioteca functools estándar. Funciones de primera clase también fueron introducidas en PHP 5.3, Visual Basic9, C#3.0 y C++11.

En Java, las clases anónimas a veces pueden ser utilizados para simular [[Clausura_(informática)|clausuras]. Sin embargo, las clases anónimas no son siempre los reemplazos completos de las clausuras, ya que tienen capacidades más limitadas. Por ejemplo, Java 8, incluye expresiones lambda para reemplazar determinadas clases anónimas. Sin embargo, la presencia de excepciones con comprobaciones en este lenguaje puede desaconsejar el uso de programación funcional, ya que puede ser necesario para capturar las excepciones que se deben controlar para después volverlas a lanzar ellos (problema este que sin embargo no se produce en otros lenguajes sobre JVM que no tienen excepciones comprobadas, como es Scala).

Muchos patrones de diseño orientado a objetos se pueden expresar en términos de programación funcional por ejemplo : el patrón de estrategia simplemente dicta el uso de una función de orden superior, y el patrón de visitantes corresponde aproximadamente a un catamorfismo, o doble también conocido como reducir, comprimir, o inyectar, se refiere a una familia de funciones de orden superior que

Page 14: Paradigma de Programación

analiza una estructura de datos recursiva y se recombinan con el uso de una operación de combinación.

Del mismo modo, la idea de los datos inmutables de la programación funcional se incluye a menudo en lenguajes de programación imperativa, por ejemplo, la tupla de Python, que es una matriz inmutable.

Ventajas de usar un paradigma funcional

Entre las ventajas que suelen citarse de usar un paradigma funcional en la programación de computadoras, están las siguientes:1

Ausencia de efectos colaterales

Proceso de depuración menos problemático

Pruebas de unidades más confiables

Mayor facilidad para la ejecución concurrente

Simulación de estados

Hay tareas (como por ejemplo, el mantenimiento del saldo de una cuenta bancaria) que a menudo parecen implementadas con estados. La programación funcional pura actúa sobre esas tareas, tareas de entrada/salida de datos tales como entrada de datos por parte del usuario y mostrar resultados por pantalla, de una forma diferente.

El lenguaje de programación funcional Haskell lo implementa usando mónadas, estructura que representa cálculos que se describen como una secuencia de pasos, derivada de la teoría de categorías.

Las mónadas ofrecen una forma de abstraer ciertos tipos de patrones computacionales, incluyendo (pero no limitado a) el diseño de operaciones con estados cambiantes (y otras acciones secundarias tales como entrada/salida de datos) de una manera imperativa sin perder la pureza. Mientras las mónadas existentes pueden ser fáciles de aplicar en un programa usando las plantillas y ejemplos adecuados, muchos estudiantes tienen problemas para entenderlo conceptualmente, por ejemplo cuando se les pide definir nuevas mónadas. (lo que a veces resulta necesario para ciertos tipos de librerías)2

Otra forma en la que los lenguajes funcionales pueden simular estados es rodeando una estructura de datos que representa el estado actual como un parámetro para llamadas a funciones. En cada llamada a función, se crea una copia de esta estructura de datos que se diferencia con el resultado de la función. Esto se conoce como “estilo de paso de estado”.

Los lenguajes funcionales no puros normalmente incluyen métodos para gestionar el cambio de estado más directamente.Clojure por ejemplo, usa una gestión de referencias que pueden ser actualizadas aplicando funciones puras al estado actual. Este tipo de enfoque permite el cambio de estado, promoviendo el uso de funciones puras como la mejor forma de realizar cálculos.

Page 15: Paradigma de Programación

Métodos alternativos como Hoare logic, el cual es un sistema formal con un conjunto de reglas lógicas que sirven para razonar con rigor acerca de la corrección de programas, y la singularidad han sido desarrollados para realizar un seguimiento de los efectos secundarios en los programas. Algunos lenguajes de investigación modernos usan sistemas de efectos para hacer explícita la presencia de efectos colaterales.

Cuestiones de eficiencia

Los lenguajes de programación son típicamente menos eficientes en el uso de CPU y memoria que los lenguajes imperativos como pueden ser C y Pascal. Esto está relacionado con el hecho de que algunas estructuras de datos de tamaño indefinido como los vectores tienen una programación muy sencilla usando el hardware existente, el cual es unamáquina de Turing bastante evolucionada. Se puede acceder muy eficientemente a las posiciones del array con CPUs con un alto grado de perfeccionamiento, haciendo pre búsquedas eficientemente a través de las memorias caché o manejado con instrucciones SIMD. Y no es fácil crear componentes homólogos inmutables de propósito general con la misma eficiencia. Para lenguajes puramente funcionales, el peor caso descendente es el logarítmico en el número de celdas de memoria usadas, porque las estructuras de memoria que cambian de tamaño pueden ser representadas por estructuras de datos puramente funcionales con tiempo de acceso logarítmico, como por ejemplo un árbol equilibrado. Sin embargo, tales retrasos no son universales. Para programas que realizan cálculos numéricos intensivos, los lenguajes funcionales tales como OCaml y Clean son algo más lentos que C. Para programas que manejan grandes matrices y bases de datos multidimensionales, los vectores de los lenguajes funcionales, como J y K, fueron diseñados optimizando su velocidad.

La inalterabilidad de los datos puede llevar en muchos casos a ejecuciones eficientes permitiendo al compilador hacer suposiciones que en un lenguaje imperativo resultarían arriesgadas, aumentando las probabilidades para la expansión en línea, que es una optimización del compilador que sustituye en el lugar de la llamada a una función con el cuerpo del destinatario de la llamada mejorando el uso del tiempo y espacio en tiempo de ejecución.

La evaluación perezosa es una estrategia de evaluación que retrasa el cálculo de una expresión hasta que su valor sea necesario, también puede mejorar la velocidad del problema, incluso asintóticamente, mientras que puede reducir la velocidad por un factor constante, sin embargo puede producir pérdidas de memoria si se usa de manera incorrecta. Launchbury 1993 discute de manera teórica los problemas relacionados con las pérdidas de memoria de evaluación perezosa, y O’Sullivan et al. 2008 da algunos consejos prácticos para el análisis y la solución de estos problemas. Sin embargo, las implementaciones más generales de evaluación perezosa hace un uso extensivo de código sin referencia y los datos llevan a cabo un funcionamiento pobre en los procesadores modernos con un alto grado de paralelismo y cachés multinivel, donde un fallo de caché puede producir un coste de cientos de ciclos de reloj.

Lenguajes funcionales

Entre los lenguajes funcionales puros, cabe destacar a Haskell y Miranda. Los lenguajes funcionales híbridos más conocidos

Page 16: Paradigma de Programación

son Scala, Lisp, Clojure, Scheme, Ocaml, SAP y Standard ML (estos dos últimos, descendientes del lenguajeML). Erlang es otro lenguaje funcional de programación concurrente. Mathematica permite la programación en múltiples estilos, pero promueve la programación funcional. R también es un lenguaje funcional dedicado a la estadística.3Recientemente Microsoft Research está trabajando en el lenguaje F# (Functional#).

Entre otros lenguajes que se podrían utilizar para programación funcional se podrían incluir a Perl, pues, aunque es un lenguaje de propósito muy general, se pueden realizar programas usando exclusivamente funciones definidas por el usuario; así como Python, como lenguaje que incorpora el paradigma funcional; o Ruby.

Estilos de codificación

Mientras que los programas imperativos tienden a proporcionar los pasos a dar por un programa, los funcionales tienden a enfatizar la composición y disposición de las funciones, sin especificar pasos de manera explícita.

Uso en la industriaLa programación funcional es más popular en el ámbito académico que en ámbitos industriales. Sin embargo se ha empezado a usar importantes lenguajes de programación funcionales en sistemas comerciales o industriales. Un ejemplo de lenguaje de programación usado en el ámbito industrial es el Erlang, el cual fue desarrollado para poner en práctica el sistema de tolerancia a fallos en las telecomunicaciones. Importantes empresas como WhatsApp, Facebook, o T-Mobile optaron por Erlang como lenguaje en alguno de sus desarrollos. Otro ejemplo de uso de los lenguajes de programación funcionales en la industria es el caso del uso del Scheme de Lisp, que fue usado como base en el desarrollo de aplicaciones para los primeros ordenadores de la firma Apple Macintosh. De hecho, hoy en día,está siendo usado para desarrollo de sistemas de simulación y de control de telescopio. Haskell, es un ejemplo de lenguaje que creó con propósito de lenguaje de investigación pero que se ha usado para el desarrollo de sistemas aeroespaciales, programación web y diseño hardware. Otros lenguajes de programación funcionales han sido usados en ámbitos comerciales y financieros.

Paradigma funcional. 

Se basa en el concepto de función (que no es más que una evolución de los predicados), de corte más matemático.

Características de la programación funcional

Históricamente, los ordenadores se han programado utilizando lenguajes muy cercanos a las peculiaridades de la propia máquina: operaciones aritméticas simples, instrucciones de acceso a memoria, etc. Un programa escrito de esta manera puede ocultar totalmente su propósito a la comprensión de un ser humano, incluso uno entrenado. Hoy día, estos lenguajes pertenecientes al paradigma de la Programación Imperativa han evolucionado de manera que ya no son tan crípticos.

En el paradigma de la programación funcional, un programa se considera una función matemática, la cual describe una relación entre una entrada y una salida y donde el concepto de estado o variable se elimina completamente. Sabemos que una función

Page 17: Paradigma de Programación

matemática es una regla que asocia a cada x de un conjunto X de valores, un único y de otro conjunto Y de valores; se representa por: f:X → Y ó por y = f(x) Si ignoramos cómo un programa computa sus datos y nos fijamos sólo en qué computa, podemos ver al mismo como una función matemática que dada una entrada, devuelve una salida; se representa por Programa : Input -> Output ó output = Programa(input). De aquí que la implementación de un lenguaje que permita crear programas con el enfoque de una función matemática, debía ser basado en el recurso función de los lenguajes de programación.. Ahora bien, entre la concepción de una función matemática y la de una función en los lenguajes de programación tradicionales, existían diferencias:

Lenguajes de programación: Hay distinción entre la definición de la función (descripción de lo que va a hacer la función mediante los parámetros formales) y la aplicación de la misma (llamada con los parámetros actuales). Las variables refieren una zona de memoria donde se almacena un valor

Matemáticas: La distinción no es clara, a menudo el término " variable independiente " se usa tanto para el parámetro formal como actual.

Ejemplo: Si se tiene la expresión "Sea x tal que f(x) = 2 “ , no se distingue con claridad si se refiere a la definición de la función constante de valor 2 o al punto x específico en el que una función f ya definida toma el valor 2. Las variables siempre se refieren a un valor y no a una localización de memoria. No hay concepto de localización de memoria y por tanto la expr. x=x+1 no tiene sentido.

La programación funcional debe por esto eliminar el concepto de variable excepto como nombre de un valor. Por lo anterior, se concibió que en la Programación Funcional la asignación no fuera permitida como instrucción.

En programación funcional pura no existen variables, sólo existen constantes, parámetros y valores, aún cuando en la práctica la mayoría de los lenguajes de programación funcionales no son puros pues retienen algunas nociones de variables y asignaciones. Como no hay variables ni asignación, tampoco existen los ciclos al estilo de los de los lenguajes tradicionales ya que los mismos trabajan con una variable de control que se va reasignando (decrementando o incrementando). Esto se logra en un lenguaje funcional mediante la recursión.

Si lo programamos funcionalmente en un lenguaje procedural como ADA, quedaría como sigue :

procedure MCD(u,v:integer):integer;

begin

if (v=0) then return u

else

return MCD(v,u mod v);

Page 18: Paradigma de Programación

end; {MCD}

Consecuencias de ausencia de variables y asignación

Otra consecuencia de la ausencia de variables y de asignación es el hecho de que el valor de cualquier función depende solamente de los valores de sus parámetros y no de llamados previos a otras o a la misma función, así como que el valor de cualquier función no depende del orden de evaluación de sus parámetros, lo cual se conoce por el nombre de transparencia referencial, lo que facilita el empleo de la programación funcional se utiliza en aplicaciones concurrentes. El hecho de que en los lenguajes funcionales no existen las variables y existe la transparencia referencial, hacen la semántica de los programas funcionales particularmente conveniente:

No existe estado, ya que no existe concepto de localización de memoria ni de variable.

El ambiente de ejecución asocia valores con nombres, no con localizaciones de memoria; una vez que un nombre entra al ambiente, su valor no cambia. Es por esto que se habla en este caso de semántica de valores, para distinguir este tipo de semántica de la usual (semántica de almacenamiento o semántica de apuntadores).

En la programación funcional, debemos estar listos para manipular funciones sin restricciones arbitrarias. En particular, las funciones deben ser vistas como valores en sí ellas, las cuales pueden ser ejecutadas por otras funciones y las cuales pueden ser también parámetros de otras funciones, esto se expresa diciendo que estas funciones son valores de primera clase.

Características

Las características más generales de la programación funcional se resumen en:

Ausencia de efectos colaterales

El valor de una expresión solo depende de los valores de sus subexpresiones, si las tiene.

Se dice que una función (f x y z) tiene un efecto colateral si los valores de x, y, y/o z cambian en el entorno de llamada durante la aplicación de la función a sus argumentos, o si alguna otra acción ocurre mientras se evalúa f.

Una función definida con todos los parámetros por valor y donde no se hacen asignaciones a las variables globales, no tiene efectos colaterales.

La mayoría de las implementaciones de LISP incorporan algunos efectos colaterales y tipos de datos integrados. Éstos han sido incluidos para hacer más sencillo un código fácilmente legible y las implementaciones eficientes.

Semántica limpia

Algunas de las características que hacen que un lenguaje sea útil y confiable son en las cuales el lenguaje significa lo que dice -no es ambiguo- y los resultados de un

Page 19: Paradigma de Programación

programa pueden verificarse. En un lenguaje funcional f(3) siempre devolverá el mismo resultado, mientras que en un lenguaje imperativo, como Pascal, éste puede no ser el caso.

El manejo del almacenamiento de los datos es implícito, ciertas operaciones asignan almacenamiento en el momento necesario y cuando se vuelve inaccesible o no referenciado se libera automáticamente, con uso de recolección de basuras, código de programa más simple y corto.

Las funciones son valores de primera clase. Eso significa que las funciones tienen la misma jerarquía que cualquier otro valor. Una función puede ser el valor de una expresión, puede pasarse como argumento y puede colocarse en una estructura de datos.

Cuestiones finales

El estilo de la programación funcional puede ser usado y lo está siendo cada vez más en lenguajes imperativos (Ejemplo: PASCAL) por las mismas razones por las cuales el uso de los lenguajes funcionales se ha incrementado: la simplicidad de la semántica y la resultante claridad de los programas. El requerimiento básico para programar funcionalmente en cualquier lenguaje es que el mismo permita la recursión así como un mecanismo de funciones general y adecuado.

Un problema típico en este estilo de programación es el costo de ejecución de los ciclos mediante la recursión. Incluso con los procesadores modernos, que reducen sustancialmente la sobrecarga de la llamada a procedimiento, las implementaciones recursivas son más lentas que las que usan los ciclos standard.

Paradigma Dirigido por eventos

La programación dirigida por eventos es un paradigma de programación en el que el flujo del programa está determinado por eventos o mensajes desde otros programas o hilos de ejecución.

Las aplicaciones desarrolladas con programación dirigida por eventos implementan un bucle principal o main loop donde se ejecutan las dos secciones principales de la aplicación: El selector de eventos y el manejador de eventos.

La mayoría de librerías para el desarrollo de aplicaciones con GUI como GTK o Qt están diseñados para ser dirigidos por eventos, de ahí el famoso sistema de ranuras y señales de Qt que no es más que un patrón de diseño observer bastante currado.

Un poco de historia

A finales de los 70, los sistemas estaban pensados para trabajar como cadenas de ensamblajedonde un programa usaba una entrada y producía una salida que sería utilizada por otro programa como entrada para producir otra salida y así hasta finalizar

Page 20: Paradigma de Programación

el proceso. Este proceso mental de construir software es la base del desarrollo estructurado.

El padre del desarrollo estructurado (que no de la programación estructurada ojo) fue Larry LeRoy Constantine bajo el ala del Instituto de Investigación de Sistemas de IBM. Uno de los mayores expertos y defensores de los métodos estructurados es Edward Yourdon, tanto que las expresiones “Yourdon” y “métodos de análisis y diseño estructurado” son sinónimos.

Constantine y Yourdon definieron nuevos modelos de control del flujo de datos implementando lo que llamaron transacciones que en realidad son un patrón de diseño de manejadores de eventos.

En el diagrama de la derecha se muestra el siguiente proceso:

Un flujo de datos invoca eventos o lo que Constantine y Yourdon llamaron “transacciones“

Un disparador los envía a manejadores especializados, Constantine y Yourdon lo llamaron “centro de transacciones“

un conjunto de manejadores que se encargan de realizar operaciones sobre el flujo de datos

El trabajo del disparador es analizar los eventos para determinar su naturaleza y entonces enviarlos al manejador adecuado que es capaz de trabajar con eventos de esa naturaleza. El disparador tiene que procesar un flujo de eventos, así que su lógica debe incluir un bucle de eventos para poder enviar un evento a un manejador y volver a escuchar a la espera de nuevos eventos que disparar.

Es común que exista un evento especial que rompa el bucle y salga de la aplicación, a ese evento se le llama evento finalizador y es muy común en todas las librerías para escribir aplicaciones GUI. También puede ocurrir que el disparador capture un evento de naturaleza desconocida o para el que no exista un manejador adecuado, en esos casos, el disparador debe descartar el evento o lanzar una excepción.

Page 21: Paradigma de Programación

En algunas ocasiones es común que el disparador y los manejadores no sean capaces de procesar los eventos con la suficiente premura conforme van llegando por lo que la mayoría de aplicaciones basadas en GUI implementan una cola de eventos. Esta lógica es muy delicada y si no se implementa bien puede ser producto de cuellos de botella fatales.

Con la aparición en los noventa de la programación orientada a objetos nuevos diseños vieron la luz como ESA (Essential Systems Analysis) y JSD (Jackson System Development) que rompieron de forma brutal con la antigua manera de pensar en métodos estructurados.

Arquitectura cliente-servidor

La arquitectura cliente-servidor es claramente una implementación del patrón de diseño de manejadores de eventos. Podemos pensar en el servidor como un disparador de eventos que espera a la escucha de una petición de un cliente que envía un evento y entonces el servidor lo re-envía a un manejador que lo procesa y devuelve un resultado.

El servidor implementa el bucle de eventos que escucha de forma continua y procesa las peticiones reenviándolas a partes más especialistas del servidor donde son procesadas y devueltas al cliente como respuesta. Si el bucle de eventos recibe una petición extraña, sencillamente la descarta y emite un mensaje de error al cliente emisor.

Programación GUI

La programación gráfica o programación de GUI es dura, sobre todo si vienes del desarrollo web. Para empezar, todo elemento que conforma la interfaz de usuario de la aplicación debe de serdefinido con detalle. Su posición, tamaño, color, aspecto, comportamiento, etc, etc. Por eso existen los IDEs y los Frameworks, para hacernos la vida más sencilla a los desarrolladores.

En segundo lugar, la programación con GUI es difícil por que existen muchos detalles y conceptos que dependen completamente del manejo de eventos. Cualquier Widget en la interfaz de usuario tiene uno o más eventos asociados y a su vez emite uno o más eventos hacia el mundo exterior. El hardware de entrada como el ratón y el teclado, también generan eventos y esos eventos también pueden ser capturados por los widgets de la interfaz de usuario.

La mayoría de herramientas de creación de interfaces gráficas de usuario se distribuyen como frameworks. Los frameworks implementan el bucle de eventos y la cola de eventos para que no tengamos que implementarlos nosotros mismos. Eso está bien, pero el mecanismo queda encerradodentro del framework y si no estamos acostumbrados a la programación dirigida por eventos algunos conceptos pueden resultar realmente confusos.

Y no solo esos bucles quedan ocultos dentro del funcionamiento del framework, sino que también quedan ocultas mecánicas básicas que es necesario comprender para llegar a tener un entendimiento claro de la magia detrás de este paradigma. Un ejemplo claro es el patrón observer.

Page 22: Paradigma de Programación

El patrón Observer

La idea principal detrás del patrón observer es que existe una entidad con estados cambiantes y una o más entidades observándola. Los observadores esperan a que la entidad observada les informe de un cambio de estado a través de un evento que puede ser de su interés, por lo que los observadores se registran con la entidad observada.

Cuando ocurre un evento, la entidad observada mira en su lista de observadores y notifica a aquellos que se registraron para recibir eventos de ese tipo. Los observadores dejaron instrucciones detalladas de como puede la entidad observada ponerse en contacto con ellos para recibir los eventos. A este patrón también se le llama El Principio de Hollywood como parodia de la típica frase de Hollywood “te llamaremos en cuanto tengamos un papel para ti“.

El sistema de ranuras y señales de Qt y el sistema de señales de GTK son ambos implementaciones del patrón de diseño Observer.

Eventos y MVC

En el patrón de diseño MVC los eventos y el patrón de diseño observer conforman el núcleo durodel funcionamiento interno del sistema. En en patrón de diseño MVC, el Modelo es un objeto que gestiona los datos y el comportamiento de la aplicación, es la entidad observable en el patrón observer.

Las Vistas se registran con el Modelo como observadores del mismo. Cuando el Controlador realiza cambios en el Modelo, notifica esos cambios a sus observadores registrados que modifican la interfaz de la aplicación.

En el patrón de diseño MVC los objetos de evento incluyen grandes cantidades de datos que viajan de una parte a otra de la aplicación a través del viejo y raído patrón de manejadores de eventos.

Programación asíncrona

La programación asíncrona pretende llevar este patrón de diseño a todos los aspectos del desarrollo de software por medio de mecanismos que articulan de una u otra forma el uso de eventos, señales y callbacks en todas las llamadas a funciones y métodos llevados a cabo por la aplicación utilizando además múltiples hilos de ejecución normalmente en una pila o piscina de hilosgestionada por un bucle de eventos extremadamente complejo.

Algunos lenguajes como .NET lo incorporan como parte del lenguaje de facto (versión 3.0 o superiores). Otros lenguajes disponen de librerías, herramientas o frameworks que implementan la programación asíncrona. Así tenemos Twisted para Python, EventMachine para Ruby, Node.js para JavaScript, y muchas otras que seguramente desconozco para muchos otros lenguajes.

En esta pequeña introducción a la programación dirigida por eventos hemos hecho un repaso por la historia de este paradigma de programación que lleva con nosotros mucho tiempo aunque para algunos es casi desconocido. En próximas

Page 23: Paradigma de Programación

entregas hablaremos sobre las diferentes herramientas a nuestra disposición a la hora de programar de forma asíncrona en escenarios donde se requiere de un tiempo de respuesta y rendimiento superior a lo normal.

Programación orientada a aspectos

La Programación Orientada a Aspectos o POA (en inglés: aspect-oriented programming) es un paradigma de programación relativamente reciente cuya intención es permitir una adecuada modularización de las aplicaciones y posibilitar una mejor separación de incumbencias.

Gracias a la POA se pueden encapsular los diferentes conceptos que componen una aplicación en entidades bien definidas, eliminando las dependencias entre cada uno de los módulos. De esta forma se consigue razonar mejor sobre los conceptos, se elimina la dispersión del código y las implementaciones resultan más comprensibles, adaptables y reusables. Varias tecnologías con nombres diferentes se encaminan a la consecución de los mismos objetivos y así, el término POA es usado para referirse a varias tecnologías relacionadas como los métodos adaptativos, los filtros de composición, la programación orientada a sujetos o la separación multidimensional de competencias.

Objetivo

El principal objetivo de la POA es la separación de las funcionalidades dentro del sistema:

Por un lado funcionalidades comunes utilizadas a lo largo de la aplicación.

Por otro lado, las funcionalidades impropias de cada módulo.

Cada funcionalidad común se encapsulará en una entidad.

Conceptos Básicos

Debido a la escasa literatura en español sobre el tema, se presenta la terminología original en inglés.

Aspect (Aspecto) es una funcionalidad transversal (cross-cutting) que se va a implementar de forma modular y separada del resto del sistema. El ejemplo más común y simple de un aspecto es el logging (registro de sucesos) dentro del sistema, ya que necesariamente afecta a todas las partes del sistema que generan un suceso.

Join point (Punto de Cruce o de Unión) es un punto de ejecución dentro del sistema donde un aspecto puede ser conectado, como una llamada a un método, el lanzamiento de una excepción o la modificación de un campo. El

Page 24: Paradigma de Programación

código del aspecto será insertado en el flujo de ejecución de la aplicación para añadir su funcionalidad.

Advice (Consejo) es la implementación del aspecto, es decir, contiene el código que implementa la nueva funcionalidad. Se insertan en la aplicación en los Puntos de Cruce.

Pointcut (Puntos de Corte) define los Consejos que se aplicarán a cada Punto de Cruce. Se especifica mediante Expresiones Regulares o mediante patrones de nombres (de clases, métodos o campos), e incluso dinámicamente en tiempo de ejecución según el valor de ciertos parámetros.

Introduction (Introducción) permite añadir métodos o atributos a clases ya existentes. Un ejemplo en el que resultaría útil es la creación de un Consejo de Auditoría que mantenga la fecha de la última modificación de un objeto, mediante una variable y un método setUltimaModificacion(fecha), que podrían ser introducidos en todas las clases (o sólo en algunas) para proporcionarles esta nueva funcionalidad.

Target (Destinatario) es la clase aconsejada, la clase que es objeto de un consejo. Sin AOP, esta clase debería contener su lógica, además de la lógica del aspecto.

Proxy (Resultante) es el objeto creado después de aplicar el Consejo al Objeto Destinatario. El resto de la aplicación únicamente tendrá que soportar al Objeto Destinatario (pre-AOP) y no al Objeto Resultante (post-AOP).

Weaving (Tejido) es el proceso de aplicar Aspectos a los Objetos Destinatarios para crear los nuevos Objetos Resultantes en los especificados Puntos de Cruce. Este proceso puede ocurrir a lo largo del ciclo de vida del Objeto Destinatario:

Aspectos en Tiempo de Compilación, que necesita un compilador especial.

Aspectos en Tiempo de Carga, los Aspectos se implementan cuando el Objeto Destinatario es cargado. Requiere un ClassLoader especial.

Aspectos en Tiempo de Ejecución.

Problemas de otros paradigmas

Muchas veces nos encontramos, a la hora de programar, con problemas que no podemos resolver de una manera adecuada con las técnicas habituales usadas en la programación imperativa o en la programación orientada a objetos. Con éstas, nos vemos forzados a tomar decisiones de diseño que repercuten de manera importante en el desarrollo de la aplicación y que nos alejan con frecuencia de otras posibilidades.

A menudo, hace falta escribir líneas de código que están distribuidas por toda o gran parte de la aplicación, para definir la lógica de cierta propiedad o comportamiento del sistema, con las consecuentes dificultades de mantenimiento y desarrollo. En inglés este problema se conoce como scattered code, que podríamos traducir

Page 25: Paradigma de Programación

como código disperso. Otro problema que puede aparecer, es que un mismo módulo implemente múltiples comportamientos o aspectos del sistema de forma simultánea. En inglés este problema se conoce como tangled code, que podríamos traducir como código enmarañado. El hecho es que hay ciertas decisiones de diseño que son difíciles de capturar, debido a que determinados problemas no se pueden encapsular claramente de igual forma que los que habitualmente se resuelven con funciones u objetos.

Desarrollo en POA

AspectC++] es un compilador que permite desarrollar aspectos en C++.

AspectJ es una extensión Java del proyecto Eclipse para ayudar en el desarrollo orientado a aspectos.

Aspect, un módulo Perl disponible en CPAN para la Programación Orientada a Aspectos (en inglés).

PHP-AOP (AOP.io) es una lib que proporciona todo el paradigma de la POA en PHP.

phpAspect es una extensión PHP para implementar el paradigma de la POA, que, mediante árboles de decisión XML, realiza el weaving del software para ser ejecutado como PHP estándar.

FLOW3 es un framework MVC de PHP incluye un módulo para poder realizar Programación orientada a Aspectos en nuevos desarrollos.

AOP con SpringFramework 2.5 es un Framework de Java que permite programar en el paradigma de Aspectos utilizando Anotación Java

Aspyct AOP es un módulo de Python que permite incluir Programación orientada a Aspectos a programas ya existentes escritos en Python o a nuevos desarrollos.

Paradigma orientado a objetos

Introducción

Actualmente una de las áreas más candentes en la industria y en el ámbito académico es la orientación a objetos. La orientación a objetos promete mejoras de amplio alcance en la forma de diseño, desarrollo y mantenimiento del software ofreciendo una solución a largo plazo a los problemas y preocupaciones que han existido desde el comienzo en el desarrollo de software: la falta de portabilidad del código y reusabilidad, código que es dificil de modificar, ciclos de desarrollo largos y tecnicas de codificacion no intuituvas.

Un lenguaje orientado a objetos ataca estos problemas. Tiene tres características basicas: debe estar basado en objetos, basado en clases y capaz de tener herencia de clases. Muchos lenguajes cumplen uno o dos de estos puntos; muchos menos cumplen los tres. La barrera más difícil de sortear es usualmente la herencia.

Page 26: Paradigma de Programación

El concepto de programación orientada a objetos (OOP) no es nuevo, lenguajes clásicos como SmallTalk se basan en ella. Dado que la OOP. se basa en la idea natural de la existencia de un mundo lleno de objetos y que la resolución del problema se realiza en términos de objetos, un lenguaje se dice que está basado en objetos si soporta objetos como una característica fundamental del mismo.

El elemento fundamental de la OOP es, como su nombre lo indica, el objeto. Podemos definir un objeto como un conjunto complejo de datos y programas que poseen estructura y forman parte de una organización.

Esta definición especifica varias propiedades importantes de los objetos. En primer lugar, un objeto no es un dato simple, sino que contiene en su interior cierto número de componentes bién estructurados. En segundo lugar, cada objeto no es un ente aislado, sino que forma parte de una organización jerárquica o de otro tipo.

ESTRUCTURA DE UN OBJETO

Un objeto puede considerarse como una especie de cápsula dividida en tres partes:

1 - RELACIONES

2 - PROPIEDADES

3 - METODOS

Cada uno de estos componentes desempeña un papel totalmente independiente:

Las relaciones permiten que el objeto se insterte en la organización y están formadas esencialmente por punteros a otros objetos.

Las propiedades distinguen un objeto determinado de los restantes que forman parte de la misma organización y tiene valores que dependen de la propiedad de que se trate. Las propiedades de un objeto pueden ser heredadas a sus descendientes en la organización.

Los métodos son las operaciones que pueden realizarse sobre el objeto, que normalmente estarán incorporados en forma de programas (código) que el objeto es capaz de ejecutar y que también pone a disposición de sus descendientes a través de la herencia.

Encapsulamiento y ocultación

Como hemos visto, cada objeto es una estructura compleja en cuyo interior hay datos y programas, todos ellos relacionados entre sí, como si estuvieran encerrados conjuntamente en una cápsula. Esta propiedad (encapsulamiento), es una de las características fundamentales en la OOP.

Los objetos son inaccesibles, e impiden que otros objetos, los usuarios, o incluso los programadores conozcan cómo está distribuída la información o qué información hay disponible. Esta propiedad de los objetos se denomina ocultación de la información.

Page 27: Paradigma de Programación

Esto no quiere decir, sin embargo, que sea imposible conocer lo necesario respecto a un objeto y a lo que contiene. Si así fuera no se podría hacer gran cosa con él. Lo que sucede es que las peticiones de información a un objeto. deben realizarse a través de mensajes dirigidos a él, con la orden de realizar la operación pertinente. La respuesta a estas ordenes será la información requerida, siempre que el objeto considere que quien envía el mensaje está autorizado para obtenerla.

El hecho de que cada objeto sea una cápsula facilita enormemente que un objeto determinado pueda ser transportado a otro punto de la organización, o incluso a otra organización totalmente diferente que precise de él. Si el objeto ha sido bien construído, sus métodos seguirán funcionando en el nuevo entorno sin problemas. Esta cualidad hace que la OOP sea muy apta para la reutilización de programas.

Organización de los objetos

En principio, los objetos forman siempre una organización jerárquica, en el sentido de que ciertos objetos son superiores a otros de cierto modo.

Existen varios tipos tipos de jerarquías: serán simples cuando su estructura pueda ser representada por medio de un "arbol". En otros casos puede ser más compleja.

En cualquier caso, sea la estructura simple o compleja, podrán distinguirse en ella tres niveles de objetos.

-La raíz de la jerarquía. Se trata de un objeto único y especial. Este se caracteríza por estar en el nivel más alto de la estructura y suele recibir un nombre muy genérico, que indica su categoría especial, como por ejemplo objeto madre, Raíz o Entidad.

-Los objetos intermedios. Son aquellos que descienden directamente de la raíz y que a su vez tienen descendientes. Representan conjuntos o clases de objetos, que pueden ser muy generales o muy especializados, según la aplicación. Normalmente reciben nombres genéricos que denotan al conjunto de objetos que representan, por ejemplo, VENTANA, CUENTA, FICHERO. En un conjunto reciben el nombre de clases o tipos si descienden de otra clase o subclase.

-Los objetos terminales. Son todos aquellos que descienden de una clase o subclase y no tienen descendientes. Suelen llamarse casos particulares, instancias o ítems porque representan los elementos del conjunto representado por la clase o subclase a la que pertenecen.

Veamos ahora en detalle los tres elementos mencionados en "Estructura de un Objeto".

1. RELACIONES

Las relaciones entre objetos son, precisamente, los enlaces que permiten a un objeto relacionarse con aquellos que forman parte de la misma organización.

Las hay de dos tipos fundamentales:

-Relaciones jerárquicas. Son esenciales para la existencia misma de la aplicación porque la construyen. Son bidireccionales, es decir, un objeto es padre de otro cuando

Page 28: Paradigma de Programación

el primer objeto se encuentra situado inmediatamente encima del segundo en la organización en la que ambos forman parte; asimismo, si un objeto es padre de otro, el segundo es hijo del primero (en la fig. 2, B es padre de D,E y F, es decir, D,E y F son hijos de B; en la fig. 3, los objetos B y C son padres de F, que a su vez es hijo de ambos).

Una organización jerárquica simple puede definirse como aquella en la que un objeto puede tener un solo padre, mientras que en una organizacion jerárquica compleja un hijo puede tener varios padres).

-Relaciones semánticas. Se refieren a las relaciones que no tienen nada que ver con la organización de la que forman parte los objetos que las establecen. Sus propiedades y consecuencia solo dependen de los objetos en sí mismos (de su significado) y no de su posición en la organización.

Se puede ver mejor con un ejemplo: supongamos que vamos a construir un diccionario informatizado que permita al usuario obtener la definición de una palabra cualquiera. Supongamos que, en dicho diccionario, las palabras son objetos y que la organización jerárquica es la que proviene de forma natural de la estructura de nuestros conocimientos sobre el mundo.

La raíz del diccionario podría llamarse TEMAS. De éste término genérico descenderán tres grandes ramas de objetos llamadas VIDA, MUNDO y HOMBRE. El primero (vida) comprenderá las ciencias biológicas: Biología y Medicina. El segundo (mundo), las ciencias de la naturaleza inerte: las Matemáticas, la Física, la Química y la Geología. El tercero (hombre) comprenderá las ciencias humanas: la Geografía, la Historia, etc.

Veamos un ejemplo: estableceremos la relación trabajo entre los objetos NEWTON y OPTICA y la interpretaremos diciendo que significa que Newton trabajó en óptica (véase la fig. 4). La relación es, evidentemente, semántica, pués no establece ninguna connotación jerárquica entre NEWTON y OPTICA y su interpretación depende exclusivamente del significado de ambos objetos.

La existencia de esta relación nos permitirá responder a preguntas como:

¿Quién trabajó en óptica?

¿En qué trabajó Newton?

¿Quien trabajó en Física?

Las dos primeras se deducen inmediatamente de la existencia de la relación trabajo. Para la tercera observamos que si Newton trabajó en óptica automáticamente sabemos que trabajó en Física, por ser óptica una rama de la Física (en nuestro diccionario, el objeto OPTICA es hijo del objeto FISICA). Entonces gracias a la OOP podemos responder a la tercera pregunta sin necesidad de establecer una relación entre NEWTON y FISICA, apoyandonos sólo en la relación definida entre NEWTON y OPTICA y en que OPTICA es hijo de FISICA. De este modo se elimina toda redundancia innecesaria y la cantidad de información que tendremos que definir para todo el diccionario será mínima.

2. PROPIEDADES

Page 29: Paradigma de Programación

Todo objeto puede tener cierto número de propiedades, cada una de las cuales tendrá, a su vez, uno o varios valores. En OOP, las propiedades corresponden a las clásicas "variables" de la programación estructurada. Son, por lo tanto, datos encapsulados dentro del objeto, junto con los métodos (programas) y las relaciones (punteros a otros objetos). Las propiedades de un objeto pueden tener un valor único o pueden contener un conjunto de valores mas o menos estructurados (matrices, vectores, listas, etc.). Además, los valores pueden ser de cualquier tipo (numérico, alfabético, etc.) si el sistema de programación lo permite.

Pero existe una diferencia con las "variables", y es que las propiedades se pueden heredar de unos objetos a otros. En consecuencia, un objeto puede tener una propiedad de maneras diferentes:

-Propiedades propias. Están formadas dentro de la cápsula del objeto.

-Propiedades heredadas. Estan definidas en un objeto diferente, antepasado de éste (padre,"abuelo", etc.). A veces estas propiedades se llaman propiedades miembro porque el objeto las posee por el mero hecho de ser miembro de una clase.

3. METODOS

Una operación que realiza acceso a los datos. Podemos definir método como un programa procedimental o procedural escrito en cualquier lenguaje, que está asociado a un objeto determinado y cuya ejecución sólo puede desencadenarse a través de un mensaje recibido por éste o por sus descendientes.

Son sinónimos de 'método' todos aquellos términos que se han aplicado tradicionalmente a los programas, como procedimiento, función, rutina, etc. Sin embargo, es conveniente utilizar el término 'método' para que se distingan claramente las propiedades especiales que adquiere un programa en el entorno OOP, que afectan fundamentalmente a la forma de invocarlo (únicamente a través de un mensaje) y a su campo de acción, limitado a un objeto y a sus descendientes, aunque posiblemente no a todos.

Si los métodos son programas, se deduce que podrían tener argumentos, o parámetros. Puesto que los métodos pueden heredarse de unos objetos a otros, un objeto puede disponer de un método de dos maneras diferentes:

-Métodos propios. Están incluídos dentro de la cápsula del objeto.

-Métodos heredados. Estan definidos en un objeto diferente, antepasado de éste (padre,"abuelo", etc.). A veces estos métodos se llaman métodos miembro porque el objeto los posee por el mero hecho de ser miembro de una clase.

Polimorfísmo

Una de las características fundamentales de la OOP es el polimorfísmo, que no es otra cosa que la posibilidad de construir varios métodos con el mismo nombre, pero con relación a la clase a la que pertenece cada uno, con comportamientos diferentes. Esto conlleva la habilidad de enviar un mismo mensaje a objetos de clases diferentes. Estos objetos recibirían el mismo mensaje global pero responderían a él de formas

Page 30: Paradigma de Programación

diferentes; por ejemplo, un mensaje "+" a un objeto ENTERO significaría suma, mientras que para un objeto STRING significaría concatenación ("pegar" strings uno seguido al otro)

Demonios

Es un tipo especial de métodos, relativamente poco frecuente en los sistemas de OOP, que se activa automáticamente cuando sucede algo especial. Es decir, es un programa, como los métodos ordinarios, pero se diferencia de estos porque su ejecución no se activa con un mensaje, sino que se desencadena autmáticamente cuando ocurre un suceso determinado: la asignación de un valor a una propiedad de un objeto, la lectura de un valor determinado, etc.

Los demonios, cuando existen, se diferencian de otros métodos por que no son heredables y porque a veces están ligados a una de las propiedades de un objeto, mas que al objeto entero.

CONSIDERACIONES FINALES

Beneficios que se obtienen del desarrollo con OOP

Día a día los costos del Hardware decrecen. Así surgen nuevas áreas de aplicación cotidianamente: procesamiento de imágenes y sonido, bases de datos multimediales, automatización de oficinas, ambientes de ingeniería de software, etc. Aún en las aplicaciones tradicionales encontramos que definir interfases hombre-máquina "a-la-Windows" suele ser bastante conveniente.

Lamentablemente, los costos de producción de software siguen aumentando; el mantenimiento y la modificación de sistemas complejos suele ser una tarea trabajosa; cada aplicación, (aunque tenga aspectos similares a otra) suele encararse como un proyecto nuevo, etc.

Todos estos problemas aún no han sido solucionados en forma completa. Pero como los objetos son portables (teóricamente) mientras que la herencia permite la reusabilidad del código orientado a objetos, es más sencillo modificar código existente porque los objetos no interaccionan excepto a través de mensajes; en consecuencia un cambio en la codificación de un objeto no afectará la operación con otro objeto siempre que los métodos respectivos permanezcan intactos. La introducción de tecnología de objetos como una herramienta concepual para analizar, diseñar e implementar aplicaciones permite obtener aplicaciones más modificables, fácilmente extendibles y a partir de componentes reusables. Esta reusabilidad del código disminuye el tiempo que se utiliza en el desarrollo y hace que el desarrollo del software sea mas intuitivo porque la gente piensa naturalmente en términos de objetos más que en términos de algoritmos de software.

Problemas derivados de la utilización de OOP en la actualidad

Un sistema orientado a objetos, por lo visto, puede parecer un paraíso virtual. El problema sin embargo surge en la implementación de tal sistema. Muchas compañías oyen acerca de los beneficios de un sistema orientado a objetos e invierten gran cantidad de recursos luego comienzan a darse cuenta que han impuesto una

Page 31: Paradigma de Programación

nueva cultura que es ajena a los programadores actuales. Específicamente los siguientes temas suelen aparecer repetidamente:

Curvas de aprendizaje largas. Un sistema orientado a objetos ve al mundo en una forma única. Involucra la conceptualización de todos los elementos de un programa, desde subsistemas a los datos, en la forma de objetos. Toda la comunicación entre los objetos debe realizarse en la forma de mensajes. Esta no es la forma en que están escritos los programas orientados a objetos actualmente; al hacer la transición a un sistema orientado a objetos la mayoría de los programadores deben capacitarse nuevamente antes de poder usarlo.

Dependencia del lenguaje. A pesar de la portabilidad conceptual de los objetos en un sistema orientado a objetos, en la práctica existen muchas dependencias. Muchos lenguajes orientados a objetos están compitiendo actualmente para dominar el mercado. Cambiar el lenguaje de implementación de un sistema orientado a objetos no es una tarea sencilla; por ejemplo C++ soporta el concepto de herencia multiple mientras que SmallTalk no lo soporta; en consecuencia la elección de un lenguaje tiene ramificaciones de diseño muy importamtes.

Determinacion de las clases. Una clase es un molde que se utiliza para crear nuevos objetos. En consecuencia es importante crear el conjunto de clases adecuado para un proyecto. Desafortunadamente la definición de las clases es más un arte que una ciencia. Si bien hay muchas jerarquías de clase predefinidas usualmente se deben crear clases específicas para la aplicación que se este desarrollando. Luego, en 6 meses ó 1 año se da cuenta que las clases que se establecieron no son posibles; en ese caso será necesario reestructurar la jerarquía de clases devastando totalmente la planificación original.

Performance. En un sistema donde todo es un objeto y toda interaccion es a través de mensajes, el tráfico de mensajes afecta la performance. A medida que la tecnología avanza y la velocidad de microprocesamiento, potencia y tamaño de la memoria aumentan, la situacion mejorará; pero en la situación actual, un diseño de una aplicación orientada a objetos que no tiene en cuenta la performance no será viable comercialmente.

Idealmente, habría una forma de atacar estos problemas eficientemente al mismo tiempo que se obtienen los beneficios del desarrollo de una estrategia orientada a objetos. Deberia existir una metodología fácil de aprender e independiente del lenguaje, y facil de reestructurar que no drene la performance del sistema .