пятница, 12 марта 2010 г.

Расширение возможностей SLIME

Сразу скажу, что вчера после коротких сомнений форкнул SLIME. Без этого программирование тех возможностей, которые мне нужны порой слишком сильно напоминает хак, чего я не люблю. Моя первоочередная цель - упрощение расширения возможностей SLIME сторонними модулями. Если ли у вас также есть какой-либо интерес в этой области (может быть он появиться после этой заметки ;)) - пишите, обсудим (либо сразу делайте форк средствами github). Но сразу предупреждаю, что возможность включения моих изменений в оригинальный SLIME весьма сомнительна.

Итак, вчера я реализовал следующую возможность: если в систему загружен RESTAS, то можно в Emacs выполнить команду M-x restas-inspect-module, ввести имя модуля (в терминологии RESTAS), например, restas.wiki, и получить стандартный инспектор, в котором показывается имя модуля и список маршрутов (routes), который предоставляет этот модуль. Пока всё, но для первой пробы пойдёт, тем более, что реализация совершенно тривиальна:

Common Lisp код
(defpackage #:restas.swank
(:use #:cl #:iter)
(:export #:inspect-module))

(in-package #:restas.swank)

(defstruct %restas-module
package)

(defmethod swank:emacs-inspect ((module %restas-module))
(let ((package (find-package (%restas-module-package module))))
`("" "Name: " (:value ,(package-name package))
(:newline)
(:newline)
"Routes: "
(:newline)
,@(iter (for route in-package (symbol-value (find-symbol restas::+routes-symbol+ package)))
(collect (list :value
(find-symbol (symbol-name route)
package)))
(collect '(:newline))))))

(swank:defslimefun inspect-module (package)
(swank:inspect-object (make-%restas-module :package package)))
elisp код
(defun restas-inspect-module (module)
"Inspect module."
(interactive (list (slime-read-package-name "Module: ")))
(when (not module)
(error "No module given"))
(slime-eval-async `(restas.swank:inspect-module ,module) 'slime-open-inspector))
Большая часть расширений SLIME может быть выполнена совершенно тривиальным образом. На стороне CL-кода необходимо объявить функцию, которая делает основную работу и возвращает результат в виде s-выражения. Для этого предусмотрен специальный макрос defslimefun, который согласно комментариям создаёт функции, которые могут использоваться из elisp-кода. Технически это не совсем верно, ибо он пока всего лишь объявляет функцию и экспортирует её из пакета. Но я думаю, что это хорошая практика использовать для объявления подобных функций специальный макрос, тем более что я собираюсь его несколько изменить, что бы он выполнял больше полезной работы (например, его можно использовать для реализации автоматической генерации описания протокола). Теперь, вызывать эту функцию на стороне elisp можно либо с помощью slime-eval (синхронный вариант), либо с помощью slime-eval-async (асинхронный вызов, требуется также указать callback-функцию для обработки результата). Это настолько просто, что может даже сложиться впечатление, что код функции restas.swank:inspect-module (см. код выше) вообще объявлен на стороне elisp (я подумывают на тем, что бы использовать defslimefun для автоматической генерации elisp-кода, так что подобные вызовы можно будет делать совершенно прозрачным образом). Теперь, полученное s-выражение можно как-либо обработать и например что-нибудь показать пользователю.

В коде выше я также использую возможность переопределения работы стандартного инспектора с помощью определения специализированного метода emacs-inspect. Это вообще является самым простым способом расширения функционала SLIME: просто определите emacs-inspect для ваших классов (или даже отдельных объектов, используя eql-спецификатор)!

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

Причём, SLIME может использоваться для создания elisp-приложений, которые не имеют непосредственного отношения к Common Lisp, но которые трудно реализовать только на elisp (например, человеческий интерфейс к базам данных). Как известно, для elisp нет FFI, но SLIME предоставляет мощный интерфейс для взаимодействия с Common Lisp кодом (а также с кодом на Scheme и Clojure), что гораздо круче ;)

2 комментария:

  1. @axiger
    Ну, сегодня у меня уже есть вообще нормальный инспектор модулей, который показывает для модуля маршруты, субмодули, функции инициализации и финализации. Из инспектор можно перейти (с помощью ".") на определение конкретного маршрута, или там функции инициализации модуля. Вообще, красота :)

    ОтветитьУдалить