Как начать писать код на Lisp?

  • Tutorial

Часто приходится видеть, как новички пробуют Common Lisp и потом жалуются, что с ним невозможно нормально работать. Как правило, это происходит из-за того, что они не понимают как настроить себе процесс, обеспечивающий тот самый "быстрый отклик" от среды разработки, когда ты поменял функцию, скомпилировал её и изменения тут же начали использоваться внутри уже "бегущей" прогрммы без её перезапуска.


Понять, как это выглядит, можно посмотрев какой-нибудь ролик на youtube, где демонстрируется интерактивная разработка на Common Lisp.



Сегодня я хочу показать, как настроить себе dev окружение для такой разработки. В 2018 году это стало совсем просто, благодаря постоянно улучшающемуся тулингу.


Заранее прошу прощения за то, что следующие ролики записаны в Asciinema, а Habrahabr его не поддерживает. Кликайте на скриншоты консоли, чтобы посмотреть ролики.


Для начала, надо установить SBCL, Roswell и Emacs. Рассказывать я буду на примере установки всего в OSX, и буду рад если в комментариях вы поделитесь своим опытом на Windows и Linux. Тогда я смогу дополнить статью примерами для всех трех платформ.


SBCL это одна из многочисленных реализаций Common Lisp. Из опенсорсных - самая быстрая. При желании, на SBCL можно запускать код по скорости сопоставимый с кодом на C++, но при этом имея все плюшки от быстрой интерактивной разработки.


Roswell, это утилита, для установки и запуска Common Lisp программ. В том числе она умеет запускать преднастроенный Emacs, а так же собирать программы в бинарники.


Emacs вы наверняка знаете - такая операционная система, в которой есть и редактор кода. Можно писать на Common Lisp и в любом другом редакторе, но на сегодняшний день у Emacs лучшая интеграция и поддержка смантического редактирования кода. С ним вам не придётся считать скобочки, он всё делает за вас.


Итак, если вы используете OSX, то нужно сделать


brew install roswell emacs

После того, как brew пошуршит диском и поставит всё нужное, просто запустите в терминале:


ros run

Эта команда автоматически поставит вам последнюю версию SBCL и стартует Lisp repl, куда можно вводить код:



Но это не дело, разрабатываться так нелья. Поэтому давайте настроим Emacs для полноценной разработки:


ros emacs

Команда запустит Emacs в консоли и настроит Quicklisp — пакетный менеджер для Common Lisp.
Но прежде чем мы продолжим, давайте настроим терминал, emacs и OSX так, чтобы они хорошо работали вместе.


Сначала надо в OSX и iTerm поменять некоторые настройки


Делаем так, чтобы CapsLock работал как Control. В Emacs без этого — никуда:



Затем отключить в шоткатах MissionControl все комбинации, связанные с использованием Control и стрелок:



Затем поставить iTerm2 и переключить в настройках профиля поведение Alt с Normal на Esc+:



После чего, создать файлик с минимальным конфигом для Emacs, ~/.emacs.d/init.el:


(package-initialize)

(require 'package)

(add-to-list 'package-archives
         '("MELPA" . "http://melpa.milkbox.net/packages/") t)

(defun install-package (package)
  (unless (package-installed-p package)
    (package-refresh-contents)
    (package-install package)))

(install-package 'paredit)
(install-package 'expand-region)

(defun setup-lisp-mode ()
  (require 'paredit)
  (paredit-mode)
  (define-key paredit-mode-map (kbd "C-w") 'paredit-backward-kill-word))

(add-hook 'lisp-mode-hook
      'setup-lisp-mode)

(add-hook 'emacs-lisp-mode-hook
      'setup-lisp-mode)

;; используем C-w для удаления слова с опечаткой и последующего набора его заново
;; вместо kill-region
(global-set-key (kbd "C-w") 'backward-kill-word)
;; вместо кучи команд начинающихся с kmacro-
(global-set-key (kbd "C-x C-k") 'kill-region)
;; вместо indent-new-comment-line
(global-set-key (kbd "M-j")
                (lambda ()
                  (interactive)
                  (join-line -1)))

;; поиск и замена
(global-set-key (kbd "C-c r s") 'replace-string)
(global-set-key (kbd "C-c r r") 'replace-regexp)

;; по этому сочетанию emacs начинает выделять формы
;; и дальше можно просто нажимать =, чтобы расширить
;; выделение на родительскую форму.
(global-set-key (kbd "C-c =") 'er/expand-region)
;; это сочетание удобно использовать с предыдущим,
;; чтобы быстро выделить и закомментировать кусок кода
(global-set-key (kbd "C-c c") 'comment-or-uncomment-region)

(global-set-key (kbd "C-c C-\\") 'goto-last-change)

(setq custom-file "~/.emacs.d/customizations.el")
(when (file-exists-p custom-file)
  (load custom-file))

После чего, снова запускаем ros emacs, жмём Alt-X и вводим команду slime. В результате получаем командную строку для ввода лисповых команд:



Теперь можно уже кодить


Но мы не будем вводить команды в репл, лучше сразу приступим к разработки микросервиса. Если нужно только API, то проще всего использовать Ningle. Если нужен более продвинутый фреймворк, типа джанги, то можно попробовать Radiance или Caveman2. Но сейчас не будем делать ничего сложного, а замутим простую HTTP апишечку.


Откройте в Emacs файл server.lisp (C-x C-f server.lisp) и начинайте писать код. Примерно так:



В итоге, у вас внутри инстанса окажется запущен вебсервер, в который на ходу можно добавлять роуты и переопределять вьюхи.


Вот весь код, для ленивых:


;; Micro-framework for building API
(ql:quickload :ningle)
;; Now ningle is installed and we need to install a clack which is analog of WSGI
;; stack from Python
;; I've pressed C-c C-c to eval this form
(ql:quickload :clack)

;; To parse json:
(ql:quickload :jonathan)

(defvar *app* (make-instance 'ningle:<app>))

(setf (ningle:route *app* "/")
      ;; in case, if you need to parse or serialize JSON,
      ;; use Jonthan library.
      (jonathan:to-json '(:foo 100500)))

(defvar *server* nil
  "This variable will store currently running server instance.")

(defun start ()
  (if *server*
      (format t "Server already started")
      (setf *server*
        (clack:clackup *app*))))

(defun stop ()
  (if *server*
      (clack:stop *server*)
      (format t "Server is not running")))

В Лиспе конструкции, которые внутри скобочек, называются “формами”. Формы, которые на верхнем и не вложены ни в какие другие, называются top-level. Такие формы можно компилировать нажав сочетание C-c C-c, когда курсор находится внутри такой формы. Если вы перебиндили CapsLock на Сontrol, то это сочетание очень удобно нажимать.


После того, как форма скомпилирована новая версия функции сразу же вступает в силу и перезапуск сервера не требуется. Так очень удобно отлаживаться и фиксить баги. Кроме того, можно настроить автоматический прогон тестов сразу после компиляции части кода, но это уже совсем другая история.


Если вам интересны ещё какие-то темы, пишите в комментариях, постараюсь сделать посты и про них.

Поделиться публикацией
Похожие публикации
Ой, у вас баннер убежал!

Ну. И что?
Реклама
Комментарии 60
    +10
    У меня другой вопрос: зачем писать на LISP, и что писать на LISP? Для чего этот язык хорош, кроме как развлечься и попытаться сломать процедурные парадигмы в своём мышлении?
      +3
      Этих причин разве недостаточно?
        +1
        На F# или Erlang тоже можно ломать процедурные парадигмы в своём мышлении, а за них ещё и платят деньги.
        Потому хотелось бы аргументов почему лучше выбрать именно LISP, а не F# или Erlang?

        PS функционально развлечься можно и на Logo и на unLambda.
          0

          Гомоиконичности в приведенных вами языках нет, как минимум:)

        +1
        Чтобы написать лисп в лисп, чтобы можно было исполнять лисп, пока пишешь на лисп. Случайно написав плагин для Emacs
          +2
          Вот тоже хотел задать этот вопрос. Есть ли реальные задачи, которые на этом языке быстрее и проще решаются или это просто академическая игрушка…
            +1
            Обычные прикладные задачи, особенно, если заказчик не до конца представляет что хочет.
            У меня на CL написано что-то типа 1С. И CL — это не функциональный а мультипарадигмальный язык программирования.
              0
              AutoCAD содержит LISP, но для его изучения лучше поставить сам AutoCAD.
                0
                Хоть я и многолетний пользователь емакса, регулярно что-то пишущий по мелочи на лиспе, я так и не понял преимущества этого языка перед типовым мейнстримным языком типа питона. И в свое время я пытался понять чего же я в упор не замечаю в лиспе, но те примеры что приводили его фанаты, все равно не позволяли понять что в нем такого особенного что можно было бы взять для ежедневной работы. Пока для себя решил, что фанаты лиспа ничем принципиально не отличаются от фанатов другого языка — выбранный язык хорошо ложится на их предпочтения.

                Лисп в емаксе удобен для сложных хозяйственных вычислений, где нужно подгонять параметры чтобы уложиться в сумму или размер, и при этом чтобы можно было кажду строчку сопровождать комментариями.
                +1
                на вопрос «что писать на лисп?» ответ простой — всё.
                А вот «зачем?» — да, вопрос.

                Хорошо язык тем, что сам язык — суть AST, всё остальное сделано на нём самом же (ну, почти). Когда начинаешь «дмать на лисп», сначала обретаешь ясное понимание, как всё вокруг работает, и далее, если интересно — никаких ограничений.

                Ломать парадигмы в голове, на мой вкус лучше хаскелем (и потом на хаскеле тоже можно писать всё). По крайней мере, после сотен тысяч строк кода, что мне довелось читать за свою жизнь, очень хочется, что бы авторы были не ближе к машине, а ближе к сути прикладной задачи.
                  –1

                  Если бы сделали Lisp в нотации JSON я стал бы первым кто стал бы на нём программировать. А пока…

                    +2
                    что такое «в нотации json»? Фигурные скобочки вместо круглых? Идентификаторы в кавычках?

                    Кажется, что написать интерпретатор лиспа в любой нотации — это такой проект-на-выходные. Не удивлюсь, если внезапный 'npm install js-notated-lisp' вытащит пакет и установит. Хотя это скорее троллейбус из буханки хлеба…
                      0
                      что такое «в нотации json»? Фигурные скобочки вместо круглых? Идентификаторы в кавычках?

                      [] это список, {} это множество. Добавьте туда функции и будет, то что надо. Куда двигаться показал JSONNET. Вы можете использовать JSONPath like запросы. Код будет компактнее чем в лиспе. Допустим, вы знаете как работает траверсер. Вы таким образом составляете запросы, чтобы получить искомый результат, не переделывая сам код, а просто расставляя данные или импортируя нужные функции извне если вам надо расширить фунционал.


                      Кажется, что написать интерпретатор лиспа в любой нотации — это такой проект-на-выходные.

                      Так только кажется. Игрушечный интепретатор можно написать, согласен. Но не более.

                        0
                        Кому-то нужен неигрушечный лисп в нотации json?

                        Мне кажется, кому сейчас нужен современный клевый лисп — все на closure перешли.
                          0
                          [] это список, {} это множество. Добавьте туда функции и будет, то что надо.

                          Почти так устроен, например, Wolfram Language. Квадратные скобки в нём — это список. Причем для того, чтобы не пугать людей, первый элемент списка пишется перед первой квадратной скобкой. И получается почти привычное f[x] вместо [f, x]. Люди пользуются и не подозревают, что пишут практически на Лиспе. :)


                          Фигурные скобки тоже есть, но это синтаксический сахар для списка, у которого первый элемент — слово List. Это что-то вроде пометки, что список содержит данные. Да и вообще, в Wolfram Language практически все — это сахар над списками.

                            0
                            Фигурные скобки тоже есть, но это синтаксический сахар для списка, у которого первый элемент — слово List

                            Здесь фигурные скобки выполняют роль разделителя контекста и замыкают объект.

                              0
                              > Причем для того, чтобы не пугать людей, первый элемент списка пишется перед первой квадратной скобкой. И получается почти привычное f[x] вместо [f, x]. Люди пользуются и не подозревают, что пишут практически на Лиспе. :)

                              Это старая тема S-expression vs M-expression. История говорит, что LISP начали создавать, имея планы ввести M-expressions для пользователей, но S-expressions внутри, но в итоге остановились на S-expressions везде (и, я бы сказал, правильно).

                              Сейчас M-expressions ещё родное средство для Kxʼовых k и q.
                      +4
                      Пока программисты на других языках решают задачу в рамках установленных их языком правил, программисты на LISP пишут DSL, чьи правила подходят для решения задачи наилучшим образом. © Нил Форд
                        +1

                        … в результате у программистов на других языках задача решена, а программисты на LISP только закончили делать DSL?

                          0
                          В результате у программистов на других языках лапшекод, который надо будет через год полностью переписать, а у программистов на Lisp лаконично сформулированная бизнеслогика, которую легко читать и поддерживать.
                            0
                            LISP тем и хорош, что написание DSL на нём — это относительно простой и быстрый процесс. Можно справиться ещё до того, как java-программист закончит набирать SimpleBeanFactoryAwareAspectInstanceFactory.
                              0
                              Языки, на которых легко писать DSL, есть еще, это не только лисп.

                              Наермиер, в рамках питона можно соорудить DSL поиск по «python dsl» вываливает кучу статей и даже видео. В той же группе, скажем и ruby.

                              Из этого направления выпадает java, и может еще какие-то…
                            +2
                            К сожалению, частые разговоры о DSL в Лисп-кругах мало подкрепляется реальными делами. Да, DSL написать можно, но в реальности пишут нечто маловразумительное на уровне все тех же многочисленных скобок. Собственно, с языком Форт подобная ситуация. Декларируется метапрограммирование и создание DSL, но реальных практических примеров подобного очень немного. На мой взгляд, тому в Лиспе есть две причины: 1) слабая существующая поддержка построения внутренних DSL, 2) необходимость для обычного разработчика быть по совместительству еще и неплохим архитектором языков.

                            Конечно же Лисп имеет смысл применять там, где не хватает готовых решений из традиционных ЯП. Но лично меня удивляет такое внимание именно к неуклюжему Common Lisp. Почему не более изящный Scheme? Добавьте его интерпретатор к тому инструментальному средству, которое востребовано в вашем рабочем проекте (на C/C++, C#, Java или ином «скучном» языке), и у вас будет вполне достойное компромиссное решение.
                              +2

                              Раз уж мы тут про DSL говорим, то при чём тут скобки?


                              Вот тебе пример из одной библиотеки которую я некоторое время назад написал. Она упрощает парсинг опций командной строки и большую часть работы делает за тебя. Прикладной код выглядит так:


                              (defmain main ((debug "Show traceback instead of short message."
                                                    :flag t)
                                             (log   "Filename to write log to.")
                                             (token "GitHub personal access token."
                                                    :env-var "TOKEN")
                                             &rest repositories)
                                "Utility to analyze github forks."
                              
                                ;; Making real work
                                (loop for repository in repositories
                                      do (analyze repository
                                                  :log log
                                                  :debug debug
                                                  :token token)))

                              Всё, что ты видишь до строчки Making real work, это DSL для описания функции, принимающей параметры командной строки. Под капотом оно разворачивается в такой вот код:


                              (progn
                               (net.didierverna.clon:defsynopsis (:postfix "REPOSITORY")
                                 (defmain/defmain::text :contents "Utility to analyze github forks.")
                                 (defmain/defmain::flag :long-name "help" :env-var nil :description
                                  "Show help on this program." :short-name "h")
                                 (defmain/defmain::flag :long-name "debug" :env-var nil :description
                                  "Show traceback instead of short message." :short-name "d")
                                 (defmain/defmain::stropt :long-name "log" :env-var nil :description
                                  "Filename to write log to." :short-name "l")
                                 (defmain/defmain::stropt :long-name "token" :env-var "TOKEN" :description
                                  "GitHub personal access token." :short-name "t"))
                               (defun main (&rest defmain/defmain::argv)
                                 (net.didierverna.clon:make-context :cmdline
                                                                    (cons "cl-info" defmain/defmain::argv))
                                 (let ((repository (net.didierverna.clon:remainder))
                                       (net.didierverna.clon:help
                                        (net.didierverna.clon:getopt :long-name "help"))
                                       (debug (net.didierverna.clon:getopt :long-name "debug"))
                                       (log (net.didierverna.clon:getopt :long-name "log"))
                                       (token (net.didierverna.clon:getopt :long-name "token")))
                                   (when net.didierverna.clon:help
                                     (net.didierverna.clon:help)
                                     (uiop/image:quit 1))
                                   (handler-bind ((t
                                                   (lambda (condition)
                                                     (uiop/image:print-condition-backtrace condition :stream
                                                                                           *error-output*)
                                                     (uiop/image:quit 1))))
                                     (loop for reporitory in (remainder)
                                           do (analyze repository :log log :debug debug :token token))))))

                              Тут видно, как задаются возможные аргументы, какие у них могут быть флаги и короткие названия, как запускается парсинг аргументов и их значения присваиваются переменным внутри функции.


                              Абстракция defmain, написанная один раз, сама задаёт разумные дефолты и избавляет меня от написания или копирования всего этого бойлерплейта в каждой программе.


                              Такой вот DSL. Реальный.

                                0
                                Скобки здесь при том, что это синтаксический рудимент Лиспа, который просочился в нотацию нашего DSL. Спасибо за пример. Заранее прошу не принимать мою критику в штыки. Заслуживает уважения уже то, что Вы выбрали Лисп в качестве инструмента разработки.

                                В коде обращает внимание использование известного макро loop, которое является частью стандарта CL. Это действительно специализированная конструкция со своим особым синтаксисом. А вот макро defmain вполне заурядно. На обычном языке, который поддерживает замыкания, можно было бы сделать аналогичный комбинатор, который развернется в нечто подобное тому, что Вы привели.

                                А примером языка, поддерживающего конструирование DSL, является Rebol с его диалектами.
                                  0
                                  При этом скобочность лиспа позволяет не писать поддержку каждого нового DSL в редакторе. Всё работает «из коробки». И манипуляции с кодом серьёзно упрощаются. Так что я бы не считал это рудиментом. Просто особенность к которой быстро привыкаешь.
                                    0
                                    По своему опыту скажу что хватает недели регулярного кодирования на лиспе чтобы привыкнуть к скобкам и воспринимать их естественно. При этом форматирование кода сильнее всего напоминает, как ни странно, Python.
                                    +1
                                    Скобки здесь при том, что это синтаксический рудимент Лиспа

                                    Скобки — это не рудимент, это преимущество лиспа. Поначалу они действительно кажутся неудобными, но стоит лишь перестроиться и начать думать "сбалансированными выражениями" вместо символов и строк, работать с кодом становится гораздо удобнее, и современные плагины для работы с Lisp (paredit, lispy) сильно помогают. Они превращают редактор текста в редактор синтаксического дерева программы. Вот небольшое демо с демонстрацией базовых возможностей paredit.


                                    После работы с Lisp начинаешь думать по-другому и в других языках: я в основном пишу на C++ в Emacs, и работа со "сбалансированными выражениями" прочно засела в моём workflow.

                                    +1

                                    Чем DSL от функции отличается?

                                      0

                                      Правилами вычисления аргументов. У функции аргументы вычисляются, как правило, в определённом порядке. В в случае DSL могут не вычисляться вовсе, а как-то трансформироваться в код, например.


                                      К примеру, loop макрос определяет язык для итерации по коллекциям и такой простой код:


                                      DBAAS> (loop for i in '(1 2 3 4 5)
                                                   when (evenp i)
                                                     collect i)

                                      Разворачивается в более сложную конструкцию, где некоторые аргументы макроса будут вычислены (1 2 3 4 5) и (evenp i), а другие использованы для генерации кода реализующего бизнес-логику. В результате получится нечто вроде этого:


                                      (block nil
                                        (let ((i nil) (#:loop-list-827 '(1 2 3 4 5)))
                                          (declare (type list #:loop-list-827))
                                          (sb-loop::with-loop-list-collection-head (#:loop-list-head-828
                                                                                    #:loop-list-tail-829)
                                            (tagbody
                                             sb-loop::next-loop
                                              (sb-loop::loop-desetq i (car #:loop-list-827))
                                              (sb-loop::loop-desetq #:loop-list-827 (cdr #:loop-list-827))
                                              (if (evenp i)
                                                  (sb-loop::loop-collect-rplacd
                                                   (#:loop-list-head-828 #:loop-list-tail-829) (list i)))
                                              (when (endp #:loop-list-827) (go sb-loop::end-loop))
                                              (go sb-loop::next-loop)
                                             sb-loop::end-loop
                                              (return-from nil
                                                (sb-loop::loop-collect-answer #:loop-list-head-828))))))
                                        0

                                        Вообще dsl можно и на функциях построить. Язык же может быть сколь угодно странным. К примеру, ORM Django или SQLAlchemy предоставляют DSL позволяющий определять модели данных и делать запросы выполняя цепочки методов.

                                          0

                                          Lisp это и так уже язык программирования.

                                      0
                                      Проект в императивном стиле, интерпретатор в функциональном. Зачем такое жесткое разграничения парадигм. К тому же СL и как императивному языку есть что сказать.
                                      Если уж хочется Java+Scheme, то есть Сlojure.
                                        +1
                                        На мой взгляд, разграничение вполне логичное и оно проходит не в области парадигм, а в области архитектуры приложения. Нижнему уровню в большинстве случаев и полагается быть императивным. Собственно, примеров, когда интерпретатор Лиспа встроен в большую программную систему для расширения возможностей последней — масса. Даже в старых компьютерных играх такое вполне практиковалось, можно вспомнить древний Zork, где использовался далект Лиспа под названием MDL или более позднюю Abuse. Я уже не говорю про Autocad, Cakewalk и проч.

                                        С другой стороны, сейчас для встраивания в приложения существуют Tcl, Python, Lua и многие-многие другие. Поэтому, кстати говоря, я призываю к критической, объективной оценке того, где действительно целесообразно применять Лисп. Ниже привожу несколько возможных вариантов.

                                        1. Универсальное текстовое представление вычислений и данных для обмена между программными модулями. Здесь фигурирует, вообще говоря, не совсем Лисп, а его существенный элемент под названием S-выражения. В каких-то случаях S-выражения могут быть предназначены и для редактирования пользователями. Например, можно реализовать синтаксис ассемблера для какой-то экзотической архитектуры в форме Лисп-подобной нотации. В большинстве случаев ассемблерный код будет порожден компилятором, но можно учесть и вариант, когда на Лисп-подобном ассемблере придется писать человеку.

                                        2. Миниатюрный диалоговый язык для удаленного управления скромным по ресурсам микроконтроллером. Существуют Лисп-подобные реализации, которые выгоднее использовать в таких условиях, чем даже Lua.

                                        3. Средство для метапрограммирования и программных трансформаций. Тот, достаточно редкий, случай, когда важнейшим свойством Лиспа программа=данные действительно необходимо воспользоваться. Например, если требуется какая-то совершенно экзотическая программная конструкция, поддержки которой еще нет ни у Google, ни у Microsoft. «Нам совершенно необходим встроенный движок для программирования в ограничениях!» Здесь, однако, нужно серьезно поразмыслить, а нужно ли все это городить именно в рамках Лиспа.

                                        Как можно видеть, использование Common Lisp не слишком-то увязывается с описанными выше пунктами. К CL у работодателя могут возникнуть резонные вопросы на тему того, кто будет поддерживать систему после Вас, стоит ли в целом овчинка выделки. В этом смысле показательны примеры игровой компании Naughty Dog и Пола Грэма с Yahoo. Другое дело, что если просто _нравится_ писать на CL, да и условия позволяют — почему бы и нет :)
                                          0
                                          Во всех моих проектах в качестве нижнего уровня использовался мультипарадигмальный язык. А в качестве встроенного интерпретатора язык на который знают или легко способны выучить большинство программистов. Получилось так Rust+Lua, Scala+(Lua и JavaScript). В случаи с СL он и нижний и верхний уровень.
                                    0

                                    Lisp нужен для того, чтобы писать на нем конфиги к Emacs)

                                      0
                                      зачем писать на LISP, и что писать на LISP?

                                      Common Lisp – очень неплохо спроектированный язык. Да, в стандарте есть несколько моментов, которые сейчас не особо актуальны, но в целом язык очень интересный. Честно говоря, я с трудом переношу языки с динамической типизацией (Python и JavaScript снятся мне в кошмарах), но на CL я бы с удовольствием попробовал написать проект.
                                      Для CL есть качественные компиляторы, в языке есть возможность указать типы, если хочется (да, аналог gradual typing был давным давно). Код, который генерит, к примеру, SBCL, на удивление быстр (я сабмитил решения для computer language benchmark game, мой код для SBCL иногда работает ощутимо шустрее, чем мой код для Haskell, оба решения были в топе).


                                      Slime + Emacs – это приятная и мощная среда разработки, в которую вложено много человеко-лет труда. Её действительно иногда нетривиально настроить (нужно разобраться в терминологии, ASDF, системы, вот это всё). К примеру, компиляция совершенно не мешает разработке, ведь можно инкрементально компилировать код по одной функции за раз. При этом можно попросить компилятор показать ассемблерный код любой функции и посмотреть, как добавление аннотаций типов влияет на код, производимый компилятором.


                                      Ну и если в проекте нужна генерация кода, CL тут просто вне конкуренции.


                                      Единственное, что я бы не стал писать на CL – мелкие утилиты командной строки. Модель исполнения с тяжёлым долго-живущим образом не особо к этому располагает.


                                      К слову, у языка довольно много активных пользователей.  Например, бэкэнд ITA (ныне Google Flights) в значительной степени написан на CL.

                                        0
                                        А компиляторы лиспа по настоящему компилируют или там в экзешнике байткод и виртуальная машина?
                                          0

                                          Есть разные варианты.

                                            +1
                                            Когда-то, на заре развития сайта StackOverflov, я задавал там подобный вопрос. Несмотря на то, что я сам не очень понимал, что именно спрашиваю, топовый ответ там, имхо, очень крут.
                                              0
                                              Да, ответ отличный. Спасибо за ссылку!
                                          0
                                          Хотя бы, потому что это Лисп!
                                          Заголовок спойлера
                                          image
                                          0
                                          Менеджеры пакетов работают по https или подписывают библиотеки? Пробовал изучить липс. Что бы запустить сервер, требовалось скачать либу. Насторожило что все качалось по http
                                            0
                                            Проверил. Да, по HTTP. Но ведь если параноить, то надо параноить по полной – вычитывать весь код всех своих opensource зависимостей и их зависимостей, и зависимостей их зависимостей.

                                            Кстати, opensource хорош тем, что его можно исправлять. Пока завёл issue про https: github.com/quicklisp/quicklisp-client/issues/167
                                              0

                                              Про паранойю неверная логика. Всегда есть шанс что вас убьёт молния, но это же не повод не мыть руки перед едой. Пересылать программы по http не вредно до первого роутерного ботнета. Или открытого вайфая. А раз уж http стоит по умолчанию, то это очень очевидная уязвимость которую вирусописателям возможно даже выгодно эксплуатировать.

                                                0
                                                Я замечал подмены пакетов дебиан, но они не ставились из-за неверной подписи
                                            0

                                            У Common LISP есть какие либо преимущества перед Clojure? Или хотя бы то чего не в Clojure, может кто рассказать?

                                              +1
                                              Это очень холиварный вопрос :) Первое, что приходит мне в голову — SBCL может компилировать программы в исполняемые файлы и у него есть настоящая оптимизация хвостовой рекурсии.
                                                +1
                                                CL — мультипарадигмальный язык, CLojure — функциональный. В СL есть CLOS система объектно-ориентированного программирования. В CL есть макросы чтения. Скомпилированная программа на CL загружается мгновенно. в отличии от программы на Clojure.
                                                  0

                                                  В Clojure есть ООП, за счёт полной совместимости с Java. Хотя я не представляю зачем это может понадобиться кроме обратной совместимости.
                                                  Макросы чтения в Clojure тоже есть если я правильно разобрался что это означает — можно экранировать выражение используя ` и оно не вычислиться.


                                                  Скомпилированная программа на CL загружается мгновенно.

                                                  Это особенности платформы JVM, на Node.js ClojureScript так же мгновенно запускается.

                                                    +1
                                                    Нет, в Clojure нет полноценного ООП. Нет, Вы не разобрались с макросами чтения. Нет, это не только оcособенности платформы JVM но и особенности реализации самого Clojure. Напишите helloworld на Java и на Clojure и увидите разницу при загрузке.
                                                      +1

                                                      Вы все не то пишете, в Кложе нет ООП, только мульти-методы и протоколы, которые близки к идее "пусть объект знает, как сделать это". Напротив, в CL реализована мощная система полноценного ООП, CLOS (commo Lisp object system). Про макросы тоже неверно.

                                                        0
                                                        Java-style OOP с CLOS сравнивать как-то даже не смешно. Не, понятно, на ООП в принципе свет клином не сошёлся, но в некоторых случаях мощная система ООП очень даже не мешает.
                                                          0
                                                          макросы чтения это чуть больше, чем квазицитирование, вот более интересный пример, чем обратная кавычка:
                                                          UTILS> (set-dispatch-macro-character
                                                          	#\# #\s
                                                          	#'(lambda (stream sb1 sb2)
                                                          	    (declare (ignore sb1 sb2))
                                                          	    (let ((cmd (with-output-to-string (out)
                                                          			 (do ((prev (read-char stream) curr)
                                                          			      (curr (read-char stream)
                                                          				    (read-char stream)))
                                                          			     ((and (char= curr #\#)
                                                          				   (char= prev #\#)))
                                                          			   (write-char prev out)))))
                                                          	      (uiop:run-program cmd :output :string))))
                                                          T
                                                          UTILS> #s ls ~/quicklisp/ ##
                                                          "asdf.lisp
                                                          client-info.sexp
                                                          dists
                                                          local-projects
                                                          quicklisp
                                                          retired
                                                          setup.lisp
                                                          tmp
                                                          "
                                                          
                                                      +5
                                                      Мой взгляд на использование Common Lisp — как человека, который использовал его в продакшне, например, для управления промышленными ускорителями электронов.

                                                      В каких случаях вам НЕ надо писать на Common Lisp:
                                                      1. Если подобная вашей задача уже решалась уже много раз и вам, грубо говоря, надо просто оттранслировать ТЗ в код
                                                      2. Если вы не хотите испытывать сложностей с поиском разработчиков на ваш проект
                                                      3. Если ваш проект сильно зависит от большого количества сторонних библиотек (соотношение «собственного мяса» в пользу batteries сильно в пользу второго)
                                                      4. Если вы хотите создать OpenSource проект с обширным community

                                                      В каких случаях вам надо использовать Common Lisp:
                                                      1. Если так уж получилось, хоть так получаться и не должно (но всё равно получается...), что вы единственный разработчик в проекте, и нет больших шансов изменить это в будущем — эффективность языка здесь вам очень поможет;
                                                      2. Если вы вообще не представляете, с какого конца подойти к решению стоящей перед вами задачи — exploratory programming — очень сильная сторона CL;
                                                      3. Если проект требует активного использования DSL — макросы очень помогут;
                                                      4. Если вас не пугает нехватка библиотек;
                                                      5. Если вы хотите сделать прототип, который потом можно было бы переписать на другом языке.

                                                      Понимаю, что это означает не слишком обширную применимость Common Lisp'а. Я, например, по работе сейчас в основном пишу на Go, и пишу довольно много. Но при всём при этом ни Go, ни Python, C# и C++ перед этим, в отличие от CL, так и не стали для меня языками, на которых мне было бы удобно думать.
                                                        +1

                                                        Все проблемы с библиотеками решены в Clojure.

                                                          0
                                                          За что приходится платить, например, отсутствием CLOS / MOP, зависимостью от JVM и тд. К тому же в принципе без дополнительных обёрток использование нелисповских библиотек в лиспах обычно приводит к довольно… хм… своеобразному коду. Мало иметь возможность использовать библиотеку, надо, чтобы она сочеталась с конструкциями языка. Так-то в CL можно тоже библиотеки для C легко использовать, а в Сlasp, например, и C++. В принципе, срач разводить на эту тему не хочется, но у меня как-то Clojure не зашёл. Хотя, наверное, если JVM для вас комфортен, Clojure в каких-то случаях может быть и лучше.
                                                            0
                                                            Я лет 5 использовал Clojure в продакшен. Но сейчас использую Scala или CL и по возможности, перевожу написанное на эти языки.
                                                              0

                                                              Scala, мне не понятно чем он так хорош, на мой взгляд только геморой. То что можно написать на Clojure быстро и хорошо на скала потребует кучу времени и плясок с типами. Это из опыта в продакшене а не домашних хелоуворлдов.
                                                              По CL, не могу говорить т.к. его не знаю могу опираться только на clojure, те особенности что вы перечислили я для себя не могу отнести к преимуществам. Интеграция с JVM для меня огромный плюс, думаю это обьективно, есть сомнения что доступность библиотек и базовых возможностей необходимых для современной разработки хоть немного близка — поддержка многопоточной среды, средства отладки и мониторинга. Хотя может вам это не нужно. Но я не представяю как возможны современные серверные приложения без этого.

                                                        0
                                                        Вступать в религиозную дискуссию динамическая типизация против статической я не буду.
                                                        Попробуйте «быстро и хорошо» написать на Clojure приложение для Android. На Scala я пишу клиентские приложения, которые без изменения кода работают на рабочем столе, в браузере и на мобильном устройстве. Серверные приложения спокойно пишу и на Scala и на СL и на Rust. У каждого из этих мультипарадигмальных языков есть свои прелести. Интеграция с JVM иногда плюс иногда минус, зависит от задач.

                                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                        Самое читаемое