- PVSM.RU - https://www.pvsm.ru -

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

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

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

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

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

Для начала, надо установить SBCL [1], Roswell [2] и Emacs [3]. Рассказывать я буду на примере установки всего в 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, куда можно вводить код:

Как начать писать код на Lisp? - 1 [4]

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

ros emacs

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

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

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

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

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

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

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

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

После чего, создать файлик с минимальным конфигом для 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. В результате получаем командную строку для ввода лисповых команд:

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

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

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

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

Как начать писать код на Lisp? - 6 [8]

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

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

;; 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, то это сочетание очень удобно нажимать.

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

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

Автор: Александр Артёменко

Источник [9]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/tutorial/281722

Ссылки в тексте:

[1] SBCL: http://www.sbcl.org/

[2] Roswell: https://github.com/roswell/roswell

[3] Emacs: https://www.gnu.org/software/emacs/

[4] Image: https://asciinema.org/a/142424

[5] Ningle: http://8arrow.org/ningle/

[6] Radiance: https://shirakumo.github.io/radiance/

[7] Caveman2: http://8arrow.org/caveman/

[8] Image: https://asciinema.org/a/142437

[9] Источник: https://habr.com/post/413061/?utm_source=habrahabr&utm_medium=rss&utm_campaign=413061