Блог О пользователеracket-lang

Регистрация

 

Почему Lisp


Здесь я хочу сравнить семейство Lisp'ов с остальными языками программирования.

Итак:

  1. Арифметика. В Lisp есть бесконечная целочисленная арифметика (то есть, например, произведение чисел от 1 до 1000 — обычное число в отличие от C++ и Java). Также реализована точная рациональная арифметика, что позволяет производить большую часть вычислений без потери точности совсем, а если участвуют иррациональные числа, то с заданной точностью. В Си аналогичная возможность доступна только с библиотекой типа GMP.
  2. Управление памятью. Во всех лиспах есть сборка мусора. Во многих также есть слабые указатели, которые позволяют создавать ассоциативные массивы хранящие данные только до тех пор, пока хоть что-нибудь ссылается на ключ.
  3. Динамичность. Переменная может хранить любое значение. Функции, классы, методы классов можно добавлять и изменять не прерывая выполнения программы.
  4. Скорость. В среднем в два-три раза медленнее Java, в десять раз быстрее, чем Python или Perl.
  5. Синтаксис. В Lisp все выражения имеют структуру (команда параметр1 параметр2 … параметрn). То есть открывающая скобка, команда с параметрами, разделёнными пробелами и закрывающая скобка. Это на первый взляд непривычно. (a+b+c)*(d+e) будет записываться как (* (+ a b c) (+ d e)). С другой стороны, влух мы говорим как в лиспе «произведение суммы a,b,c и суммы d и e».
  6. Макросы. Макрос — функция преобразующая свои параметры в кусок кода. У Lisp здесь два преимущества: синтаксис позволяет кусок кода и параметры представлять в виде списков, а языковая сред позволяет в макросе использовать все функции, доступные в Lisp (включая использование переменных). Это позволяет, например, сделать работу с ООП в виде библиотеки (синтаксис определения класса, методов, … описывается макросами). Также можно генерировать код по внешним условиям: например, класс для доступа к БД по структуре таблицы в той БД. В других языках аналогичные цели достигаются с помощью IDE, что не даёт нужной гибкости.
  7. ООП. В Common Lisp реализована идея обобщённых функций (CLOS). Классы определяются отдельно. Методы классов могут быть определены (или изменены) позднее. Более того, метод может принадлежать не одному классу, а нескольким (проверка класса идёт по всем аргументам метода). Аналогичная функциональность в C++ достигается только паттерном Visitor (для двух классов) или через ручной перебор классов в методе. Также есть возможность указать действие до-, после- или вокруг основного действия метода, что позволяет легко добавлять проверку входных данных и журналировние к уже существующим иерархиям классов. В остальных лиспах аналог CLOS реализуется через макросы. Также есть ООП библиотеки с другой семантикой (передача сообщений и класс = пространоство имен, например).
  8. Особые переменные или параметры. В Common Lisp есть возможность указать, что переменная «особая» (special). В этом случае есть возможность указать значение переменной для заданного блока программы (и всех функций, которые вызываются из этого блока). Таким образом сочетаются достоинства глобальных переменных (можно передавать значение в функцию не указывая его явно в параметрах) без их недостатков (особая переменная меняет значение только в блоке, при доступе из другого потока или любом выходе из блока, в том числе при исключительной ситуации, значение особой переменной восстанавливается, блоки могут быть вложенными). В Racket особые переменные называются параметрами и имеют синтаксис функции (но семантика та же).
  9. Обработка исключительных ситуаций. В Lisp'ах традиционно при обработке исключительной ситуации, как правило, есть возможность не только освободить ресурсы и вернуть значение, но вместо этого указать, что можно продолжить работы с другими условиями. Например, при ошибке при чтении таблицы из файла можно пропустить значение, пропустить строку, повторить чтение файла сначала, прервать чтение.
  10. Удобная работа с замыканиями. Замыкания — это функция (или несколько) со ссылками на переменные, в окружении которых она определена. Позволяет действительно инкапсулировать данные (область видимости у переменных только внутри заданной функции и всё). На самом деле аналогичная задача решается и другими средствами. Для функции в Си можно сделать переменные с параметром static. Для нескольких функции или для замыкания, возвращаемого из функции можно сделать класс и объект соответственно. У замыканий есть два преимущества: простота написания (никаких лишних сущностей наподобие имени класса) и то, что они являются функциями. То есть везде, где можно передать функцию, можно передать замыкание. С методом класса такой фокус не проходит.

Таким образом, очевидно, что Lisp имеет гораздо больше возможностей и позволяет писать гораздо более надёжные программы. Так почему же он не завоевал популярности? Есть несколько причин:

  1. Историческая (суеверная). На Лиспе писали систему искуственного интеллекта. Поставленная задача решена не была. Разработчики назвали язык одной из причин неудачи. После этого ни один руководитель не рисковал выбрать Лисп для своего проекта.
  2. Синтаксис. Он непохож на Бейсик. Он непривычен для новичков. Более того, он расширяем, что значит, что в каждом проекте синтаксис может быть слегка другим.
  3. Библиотеки. На самом деле, нет библиотек, потому что нет популярности, а из-за этого нет библиотек. Это не критичная проблема, так как все современные лиспы могут использовать библиотеки, написанные на Си.

Ну и до недавнего времени не было хороших бесплатных компиляторов. Сейчас есть SBCL (Common Lisp), Racket (диалект Scheme), ABCL (Common Lisp на Java VM).


 

Для ответа с цитированием необходимо
выделить часть текста исходной записи