11 нояб. 2011 г.

Введение в функциональное реактивное программирование с помощью reactive-banana

Функциональное реактивное программирование (ФРП) - это метод моделирования реактивного (т.е. зависящего от времени и внешних воздействий) поведения в чистых функциональных языках. ФРП позволяет моделировать системы, реагирующие на изменяющиеся входные воздействия, в простом декларативном стиле[1].

Цель данного поста показать, как можно писать GUI на Haskell в декларативном стиле. С помощью библиотеки reactive-banana мы напишем простой счётчик, который будет управляться двумя кнопками.

29 июн. 2009 г.

StumpWM + Jabber.el

Захотел получать оповещения о приходе в Jabber.el сообщений.Для этого сначала добавил в Jabber.el возможность отправки сообщений в StumpWM.
  1. Создал в директории с Jabber.el файл jabber-stumpwm.el следующего содержания:
    (eval-when-compile (require 'jabber-alert))

    (defun jabber-stumpwm-message (msg)
    "Show MSG in StumpWM"
    ;; Possible errors include not finding the stumpish (STUMPwm Interactive SHell).
    (condition-case e
    (let ((process-connection-type))
    (call-process "stumpish" nil 0 nil "notifications-add" msg))
    (error nil)))

    (define-jabber-alert stumpwm "Show a message through the StumpWM window manager"
    'jabber-stumpwm-message)
    Данный файл - копия jabber-ratpoison.el с небольшими изменениями для StumpWM. Для отправки оповещения используется STUMPwm Interactive SHell, которой в качестве аргументов передаем имя фукции оповещения и сообщение.

  2. Добавил (require 'jabber-stumpwm) в секцию External notifiers в файле jabber.el.
Функции оповещения предоставляются модулем notifications. Для его подключения в файл ~/.stumpwmrc прописываем следующее:
(load-module "notifications")
(define-key *root-map* (kbd "N") '*notifications-map*)
А к строке *screen-mode-line-format* добавляем %N.
Результат:




Полученный результат меня до конца не устроил:
- оповещения приходится удалять вручную;
- оповещения захламляют и без того перегруженную mode-line.

Пришлось внести небольшие изменения в модуль notifications, чтобы добиться желаемого:
- добавлена возможность удаления оповещений по таймауту;
- оповещения выводятся как стандартные сообщения StumpWM.
*** notifications.lisp.old 2009-06-30 15:02:31.851810478 +0400
--- notifications.lisp 2009-06-30 15:34:10.667560769 +0400
***************
*** 75,90 ****
--- 75,107 ----
(defvar notifications nil
"A list of notification strings.")

+ (defvar *notification-time* 20
+ "This variable controls how many seconds the notification will be
+ stored in the list of notifications. Don't use timer for
+ notification if the value is nil.")
+
+ (defvar *message-timeout* 1
+ "This variable controls how many seconds elapse between each update
+ of the message.")
+
+ (defvar *message-timer* nil
+ "The timer that updates the message.")
+
(defcommand notifications-add (str)
((:rest "Notification: "))
"Add a notification string.
If a notification is already included, it will be moved to the front instead of
added anew."
+ (when *notification-time*
+ (run-notification-timer))
(when (not (string= (car notifications) str))
(when (member str notifications :test #'string=)
(setf notifications (delete str notifications :test #'string=)))
(push str notifications)))

+ (defun run-notification-timer ()
+ (run-with-timer *notification-time* nil #'notifications-delete-last))
+
(defcommand notifications-reset ()
()
"Clear all notifications."
***************
*** 105,110 ****
--- 122,142 ----
"Delete the first notification."
(setf notifications (nreverse (cdr (nreverse notifications)))))

+ (defcommand notifications-start-messages ()
+ ()
+ "Start showing messages with notifications."
+ (setf *message-timer*
+ (run-with-timer 0
+ *message-timeout*
+ (lambda ()
+ (when notifications
+ (message-no-timeout (format nil "~{~a~^~%~}" notifications)))))))
+
+ (defcommand notifications-stop-messages ()
+ ()
+ "Stop showing messages with notifications."
+ (cancel-timer *message-timer*))
+
(defun notifications-as-string (&rest r)
(declare (ignore r))
(if notifications
***************
*** 126,131 ****
--- 158,165 ----
(define-key m (kbd "d") "notifications-delete-first")
(define-key m (kbd "D") "notifications-delete-last")
(define-key m (kbd "s") "notifications-show")
+ (define-key m (kbd "m") "notifications-start-messages")
+ (define-key m (kbd "M") "notifications-stop-messages")
m))

;; Local Variables:

После выполнения функции notifications-start-messages при получении сообщений получаем следующее:

15 мая 2009 г.

StumpWM

Установил и начал настраивать под себя StumpWM - фреймовый оконный менеджер, написанный на Common Lisp. Чтобы ознакомиться с некоторыми его возможностями, можно посмотреть видео ролик.

Установка


Зависимости:
  1. реализация Common Lisp (поддерживаются clisp и SBCL)
  2. clx
  3. cl-ppcre
  4. autoconf
  5. texinfo
Я использовал SBCL 1.0.19, пакеты clx и cl-ppcre установливал с помощью clbuild:

$ darcs get http://common-lisp.net/project/clbuild/clbuild
$ cd clbuild
clbuild$ chmod +x clbuild
clbuild$ ./clbuild install clx cl-ppcre

Чтобы asdf видел пакеты, установленные с помощью, clbuild добавим следующую строку в ~/.sbclrc:

(push #p"/path/to/clbuild/systems/" asdf:*central-registry*)

Устанавливаем последнюю версию StumpWM из репозитория:

$ git clone git://git.savannah.nongnu.org/stumpwm.git
$ cd stumpwm
stumpwm$ autoconf
stumpwm$ ./configure && make

В текущей директории появился файл stumpwm, который нужно указать в ~/.xinitrc:

$ echo "exec /path/to/stumpwm" > ~/.xinitrc
$ startx

Настройка

При запуске StumpWM использует файл ~/.stumpwmrc. Например, поместив следущие инструкции в ~/.stumpwmrc мы определим функцию, которая вызывается при нажатии C-t f. Она запускает Firefox, если он ещё не был запущен, в противном случае - переходит к ранее запущенному.
(defcommand firefox () ()
"Start/Switchto Firefox."
(run-or-raise "firefox" '(:class "Firefox")))

(define-key *root-map* (kbd "f") "firefox")

В качестве примера можно использовать файл sample-stumpwmrc.lisp, можно посмотреть на файлы .stumpwmrc других пользователей, также много инересного можно узнать из stumpwm.info.

Полностью поменять поведение StumpWM можно уже в процессе работы. Для этого можно:
  1. внести изменения в ~/.stumpwmrc и выполнить команду С-t ; loadrc
  2. использовать emacs и stumpwm-mode.el
  3. использовать stumpish (STUMPwm Interactive SHell)
  4. использовать SLIME

Использование SLIME для настройки StumpWM

Установим SLIME и создадим сивольную ссылку на stumpwm.asd для clbuild:

$ cd /path/to/clbuild
clbuild$ ./clbuild install slime
clbuild$ ln -s /path/to/stumpwm.asd systems/stumpwm.asd

Для запуска SLIME необходимо добавить в .emacs следующие строки, полученные при выполнении команды $ ./clbuild slime-configuration:

(setq load-path (cons "/path/to/clbuild/source/slime" load-path))
(setq load-path (cons "/path/to/clbuild/source/slime/contrib" load-path))
(setq slime-backend "/path/to/clbuild/.swank-loader.lisp")
(setq inhibit-splash-screen t)
(load "/path/to/clbuild/source/slime/slime")
(setq inferior-lisp-program"/path/to/clbuild/clbuild --implementation sbcl lisp")
(setq slime-use-autodoc-mode nil)
(slime-setup '(slime-fancy slime-tramp slime-asdf))
(slime-require :swank-listener-hooks)

Создадим файл startstump для запуска StumpWM и swank:

;; -*-lisp-*-
(require :stumpwm)
(require :swank)
(swank-loader::setup)
(swank:create-server :dont-close t)
(stumpwm:stumpwm)

Таким образом теперь для запуска не используетсся собранный с помощью make файл stumpwm, что исключает необходимость повторных сборок при обновлении stumpwm.

Изменим ~/.xinitrc:

exec /path/to/clbuild/clbuild lisp --load /path/to/startstump

clbuild запускает реализацию Common Lisp и загружает созданный нами файл.

Выполнив startx и открыв emacs, можно выполнить команду M-x slime-connect и использовать REPL для внесения измененний. Например, следующие команды сделают стандартную mode-line более информативной:
; SLIME 2009-05-16
CL-USER> (in-package :stumpwm)
STUMPWM> (load-module "battery-portable")
T
STUMPWM> (load-module "cpu")
T
STUMPWM> (load-module "mem")
T
STUMPWM> (load-module "net")
T
STUMPWM> (setf stumpwm:*screen-mode-line-format*
(list "%n | "
'(:eval (run-shell-command "date +'%a %d %b %H:%M:%S' | tr -d [:cntrl:]" t))
" | %l| %c| %M| %B | %W"))
("%n | "
(:EVAL (RUN-SHELL-COMMAND "date +'%a %d %b %H:%M:%S' | tr -d [:cntrl:]" T))
" | %l| %c| %M| %B | %W")
STUMPWM> (mode-line)
NIL