Cuando escucho la frase "programación simbólica", inmediatamente me vienen a la mente LISP, Prolog y (sí) Mathematica. Caracterizaría un entorno de programación simbólico como uno en el que las expresiones utilizadas para representar el texto del programa también resultan ser la estructura de datos principal. Como resultado, resulta muy fácil construir abstracciones sobre abstracciones, ya que los datos se pueden transformar fácilmente en código y viceversa.
Mathematica explota esta capacidad en gran medida. Incluso más que LISP y Prolog (en mi humilde opinión).
Como ejemplo de programación simbólica, considere la siguiente secuencia de eventos. Tengo un archivo CSV que se parece a esto:
r,1,2
g,3,4
Leí ese archivo en:
Import["somefile.csv"]
¿El resultado es un código o datos? Son ambos. Son los datos que resultan de leer el archivo, pero también es la expresión que construirá esos datos. Sin embargo, según el código, esta expresión es inerte ya que el resultado de evaluarla es simplemente ella misma.
Entonces ahora aplico una transformación al resultado:
% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}
Sin detenerse en los detalles, todo lo que ha sucedido es que Disk[{...}]
se ha envuelto alrededor de los dos últimos números de cada línea de entrada. El resultado sigue siendo datos / código, pero sigue siendo inerte. Otra transformación:
% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}
Sí, todavía inerte. Sin embargo, por una notable coincidencia, este último resultado resulta ser una lista de directivas válidas en el lenguaje específico de dominio incorporado de Mathematica para gráficos. Una última transformación y empiezan a suceder cosas:
% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]
En realidad, no verías ese último resultado. En una exhibición épica de azúcar sintáctica, Mathematica mostraría esta imagen de círculos rojos y verdes:
Pero la diversión no termina ahí. Debajo de todo ese azúcar sintáctico todavía tenemos una expresión simbólica. Puedo aplicar otra regla de transformación:
% /. Red -> Black
¡Presto! El círculo rojo se volvió negro.
Es este tipo de "empuje de símbolos" lo que caracteriza a la programación simbólica. La gran mayoría de la programación de Mathematica es de esta naturaleza.
Funcional vs simbólico
No abordaré las diferencias entre programación simbólica y funcional en detalle, pero contribuiré con algunas observaciones.
Se podría ver la programación simbólica como una respuesta a la pregunta: "¿Qué pasaría si tratara de modelar todo usando solo transformaciones de expresión?" La programación funcional, por el contrario, puede verse como una respuesta a: "¿Qué pasaría si intentara modelar todo usando solo funciones?" Al igual que la programación simbólica, la programación funcional facilita la creación rápida de capas de abstracciones. El ejemplo que di aquí podría reproducirse fácilmente en, digamos, Haskell utilizando un enfoque de animación reactiva funcional. La programación funcional tiene que ver con la composición de funciones, funciones de nivel superior, combinadores, todas las cosas ingeniosas que puede hacer con funciones.
Mathematica está claramente optimizado para programación simbólica. Es posible escribir código en estilo funcional, pero las características funcionales en Mathematica son en realidad solo una fina capa sobre las transformaciones (y una abstracción con fugas en eso, vea la nota a pie de página a continuación).
Haskell está claramente optimizado para la programación funcional. Es posible escribir código en estilo simbólico, pero yo objetaría que la representación sintáctica de los programas y los datos es bastante distinta, lo que hace que la experiencia sea subóptima.
Observaciones finales
En conclusión, defiendo que existe una distinción entre la programación funcional (como la personifica Haskell) y la programación simbólica (como la personifica Mathematica). Creo que si uno estudia ambos, aprenderá sustancialmente más que estudiar solo uno: la prueba definitiva de distinción.
¿Abstracción funcional con fugas en Mathematica?
Sí, con fugas. Pruebe esto, por ejemplo:
f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]
Debidamente informado y reconocido por el WRI. La respuesta: evita el uso de Function[var, body]
( Function[body]
está bien).