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

Часто приходится видеть, как новички пробуют 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, куда можно вводить код:

hmt7qs9r7k60pg9e29ysqa5h6rm.png

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

ros emacs

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


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

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

oiepzhbmmqjcspsciiwocbvmhiu.png

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

dmm_imeldeg6ajv1vevbkeexrss.png

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

i70hr7zpgapyz_6_rcn1ix68lek.png

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

oyrw2p0znkhm6mylih7tr2f8iaw.png


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

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

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

_h3vxyvwm-d-ba4rjvyfc4f3x2e.png

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

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

;; 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:))

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

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

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

© Habrahabr.ru