Коли я чую фразу "символічне програмування", LISP, Prolog та (так) Mathematica негайно спливають на думку. Я б охарактеризував символічне середовище програмування як середовище, в якому вирази, що використовуються для представлення тексту програми, також є основною структурою даних. Як результат, стає дуже легко будувати абстракції на абстракціях, оскільки дані можуть бути легко перетворені в код і навпаки.
Mathematica активно використовує цю можливість. Навіть сильніше, ніж LISP та Prolog (IMHO).
Як приклад символічного програмування розглянемо наступну послідовність подій. У мене є файл CSV, який виглядає так:
r,1,2
g,3,4
Я прочитав цей файл у:
Import["somefile.csv"]
Дані результату чи код? Це і те, і інше. Це дані, які є результатом читання файлу, але це також буває вираз, який буде будувати ці дані. Однак, як йде код, цей вираз є інертним, оскільки результат його оцінки - це просто сам.
Отже, тепер я застосовую перетворення до результату:
% /. {c_, x_, y_} :> {c, Disk[{x, y}]}
--> {{r,Disk[{1,2}]},{g,Disk[{3,4}]}}
Не зупиняючись на деталях, все, що сталося, - це те, що Disk[{...}]
було обернуто навколо останніх двох чисел з кожного рядка введення. Результатом все одно є дані / код, але все ще інертний. Ще одне перетворення:
% /. {"r" -> Red, "g" -> Green}
--> {{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}
Так, все ще інертний. Однак за дивовижним збігом обставин цей останній результат якраз і є списком дійсних директив у вбудованій для графіки програмі Mathematica для графіки. Останнє перетворення, і речі починають відбуватися:
% /. x_ :> Graphics[x]
--> Graphics[{{Red,Disk[{1,2}]},{Green,Disk[{3,4}]}}]
Насправді, ви не побачите цього останнього результату. У епічному показі синтаксичного цукру Mathematica продемонструє таку картину червоних та зелених кіл:
Але задоволення на цьому не зупиняється. Під усім цим синтаксичним цукром ми все ще маємо символічний вираз. Я можу застосувати інше правило трансформації:
% /. Red -> Black
Престо! Червоне коло стало чорним.
Саме такий вид "натискання на символ" характеризує символічне програмування. Переважна частина програмування Mathematica має таку природу.
Функціональний проти символічний
Я не буду детально розглядати відмінності між символічним та функціональним програмуванням, але внесу кілька зауважень.
Можна розглядати символічне програмування як відповідь на запитання: "Що було б, якби я спробував змоделювати все, використовуючи лише перетворення виразів?" Навпаки, функціональне програмування можна розглядати як відповідь на: "Що станеться, якби я спробував змоделювати все, використовуючи лише функції?" Так само, як символічне програмування, функціональне програмування дозволяє швидко нарощувати рівні абстракцій. Наведений тут приклад можна легко відтворити, скажімо, у Haskell, використовуючи підхід функціональної реактивної анімації. Функціональне програмування - це все про склад функцій, функції вищого рівня, комбінатори - все чудове, що ви можете робити з функціями.
Mathematica чітко оптимізована для символічного програмування. Можна писати код у функціональному стилі, але функціональні особливості Mathematica насправді являють собою лише тонкий шпон над перетвореннями (і нещільна абстракція при цьому, див. Виноску нижче).
Haskell чітко оптимізований для функціонального програмування. Можна писати код у символічному стилі, але я міг би посперечатися, що синтаксичне представлення програм та даних досить різне, що робить досвід неоптимальним.
Заключні зауваження
На закінчення я виступаю за те, що існує різниця між функціональним програмуванням (втіленим Хаскеллом) та символічним програмуванням (втіленим Mathematica). Я думаю, що якщо хтось вивчить і те, і інше, то дізнаєшся значно більше, ніж вивчення лише одного - остаточного випробування відмінності.
Дірява функціональна абстракція в Mathematica?
Так, діряво. Спробуйте це, наприклад:
f[x_] := g[Function[a, x]];
g[fn_] := Module[{h}, h[a_] := fn[a]; h[0]];
f[999]
Належним чином повідомлено WRI та визнано ним. Відповідь: уникайте використання Function[var, body]
( Function[body]
це нормально).