пятница, 4 сентября 2009 г.

Наваял в первом приближении нечто, способное генерить код, похожий на PHP

Фактически это pretty-printer скобочный выражений, которые должны примерно соответсвовать языку PHP. Но! Есть macroexpand. Правда макросы определяются на host-языке, т.е. на Common Lisp, а не на target языке, т.е. в самом языке нет (пока) defmacro, macrolet, symbol-macrolet, (мне кажется что expansion code все равно надо писать на common lisp, а не на PHP, так что полезность defmacro et al. в target языке под вопросом). Глюков там еще хватает. Лежит на github'е но это пока довольно бесполезная штука. Потому что чем писать на PHP в скобочном синтаксисе, лучше писать в родном. В скобочном синтаксисе надо писать на LISP или Scheme, а это совсем другое, кроме макросов все же хочется иметь полноценные замыкания, полноценную lambda. Для этого надо делать flattening envionrment'а (не знаю как это переводится) через lambda lifting (тоже не знаю), что нафиг убъет всю читаемость сгенеренного кода. Вот уже неделю думаю, читаю умные книжки, надо такое делать или нет, или пускай будет PHP в скобочном варианте с макросами? Хотя emacs lisp ведь без замыканий живет и ничего. Надо осилить Lisp in Small Pieces, начиная от денотационной семантики и дальше, может натолкнет на какие-то мысли.

понедельник, 3 августа 2009 г.

pretty printer

А еще в Common LISP есть совершенно замечательный настраиваемый pretty-printer, который можно использовать не только для того, чтобы печатать в синтаксисе лиспа. Вот, например, как генерится код на Паскале.

четверг, 30 июля 2009 г.

Кодогенерация: Common Lisp vs. Emacs Lisp

Одно из применений лиспа - это генерация кода, в т.ч. не только на лиспе, но и на других языках. Вот я думаю, может для этой задачи ограничиться Emacs Lisp, а не тащить common lisp?

Потенциальные преимущества:

  • Emacs есть везде и под все, а Common Lisp - нет (для меня несущественно, но все же).
  • Собственно генерация кода нужна именно для автоматизации программирования, что в принципе кажется Emacs'овой задачей.
  • Язык сам по себе попроще будет, при этом, просмотрев исходники parenscript, на первый взгляд не нашел там ничего такого, что нельзя было бы сделать на Emacs Lisp. Местами используется CLOS, но это несущественно.

Недостатки:
  • В Emacs Lisp нет READ-макросов, что несколько ограничивает создание DSL, и делает невозможными красоты типа CL-INTERPOL
  • При макроподстановке не выполняется destructuring, (собственно он в Emacs Lisp обнаружен не был), что тоже может быть неудобно.
  • Отсутствие lexical closures в основной верии Emacs, lexical-let не в счет - не могу понять последствия для этой задачи.

четверг, 25 июня 2009 г.

Читаю книжку Programming Clojure

Нужно написать нечто, и кажется что на JVM это нечто будет работать лучше всего. Хотя проект не терпит всякие "альтерниативные" языки в production коде, так как мои знания Джавы мягко говоря не очень, то мне или долго писать на Java, или выучить Clojure и быстро на нем переписать прототип, ранее сделанный на Сommon LISP, чтобы быстрее заняться другими делами, и если все будет хорошо, потом уже аккуратно сделать Java вараинт.

Книжка не очень большая, меньше 300 страниц, и легко читается, по крайней мере первые 4 главы прочел уже довольно быстро. Сам Clojure хорош (по сравнению с Java так вообще, но ... если вам не надо Java, то возьмите лучше Common LISP. Из JVM-hosted языков знаком немного с Jython, и отличие вижу в том, что в то время как Jython старается как можно больше быть похожим на C Python, Clojure этим не страдает, его задача - максимально легко использовать Java с помошью приятного функционального язычка, а на что оно похоже - не важно.

четверг, 18 июня 2009 г.

Closure Oriented Programming

Closure Oriented Programming (from http://letoverlambda.com/) hinders interactive development. I find it much easier to have separate defuns operating on complex data structures than function that are deeply nested within a "network" of closures. Despite we have fewer code lines with closures, than with declaring separate structs or classes, with more traditional approach, the code and data is much easier to explore from REPL.

вторник, 9 июня 2009 г.

let*

Почему-то не нравится let*. Хочется писать вложенные let, хоть это и больше скобок и индентации.

пятница, 5 июня 2009 г.

SXHASH

Странно работает SXHASH. Если считать хеш символа, то в 32-bit SBCL значения идут чуть ли не подряд (fixnum в районе 300 тыс. ). В Clozure CL вообще кажется идут ASCII коды символов! Если я взял символы от A до Z, как мне получить значения хеша, равномено распределенные в некотором интервале?

четверг, 4 июня 2009 г.

SLIME и PACKAGES

Обнаружил, что надо в файле обяазательно писать (in-package :mypacakge), чтобы из буфера slime-compile-defun и прочие делали это в правильном пекедже. SLIME просто ищет in-package в тексте буфера.

пятница, 29 мая 2009 г.

Книги по ЛИСПу

Хочется самые правильные книги по LISP проработать качественно. А именно хочется прорешать все упражнения. У Эли Бендерского заняло чуть меньше года прорешать весь SICP на Common LISP. У меня прорешать его на Scheme заняло месяца 2 или 3. Но во первых не все, а где-то три четверти, и я довольно плотно за него тогда взялся. Сейчас так не получится, так что надо рассчитывать, что PAIP со всем упражениями займет не меньше чем полгода, а то и год. Чтобы все это мне не надоело, его надо будет это чередовать с другими книжками, так что, надеюсь, On LISP и Let Over Lambda будут прочитаны гораздо раньше чем PAIP. Остается домучить LISP in Small Pieces, и AMOP. Еще есть в электронном виде какая-то книжица по CLOS, даже не знаю стоит ли она прочтения. Вобщем, минимум на год вперед чтения по LISP'у хватает, быстрее все осилить может и можно, но зачем?

Обзор Common LISP

Прослушал обзорный доклад Всеволода о среде Сommon LISP' а, включая особенности языка, ИДЕ, библиотеки, коммьюнити и пр. Я ничего не ожидал от этого доклада, ну что еще можно сказать. На удивление, презентация была очень хорошо подготовлена, и оказалась рассчитанной примерно на мой уровень, уровень человека, который уже успел попробовать Common LISP на практике, но еще маловато знает. В презентации оказалось полно buzzwords, которые нужно знать, я надеюсь, он ее выложит, и там будут кликабельные ссылки.

вторник, 26 мая 2009 г.

Guide to Lisp Style

Хочется выписать отдельно тут:

  • Be specific
  • Use abstractions
  • Be concise
  • Use the provided tools
  • Don't be obscure
  • Be consistent

PAIP

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

четверг, 21 мая 2009 г.

Немного отвлекся от Common LISP и наваял первую наивную прогу на Emacs Lisp :)

Берет бинарный файл, и оформляет в виде C-шного массива:


(defun file-to-c-array (filename)
"convert a binary file into a C array of characters"
(interactive "fFile Name: ")
(let* ((buf (find-file-noselect filename))
(oldbuf (current-buffer)))
(save-excursion
(insert "\nconst unsigned char key[] = {")
(set-buffer buf)
(beginning-of-buffer)
(let ((i 0))
(while (not (eobp))
(let* ((c (following-char)))
(forward-char)
(set-buffer oldbuf)
(when (zerop (mod i 8))
(insert "\n\t"))
(incf i)
(insert (format "0x%x, " c))
(set-buffer buf))))
(set-buffer oldbuf)
(delete-backward-char 2)
(insert "\n};\n"))))

пятница, 15 мая 2009 г.

Насчет FFI - не все так хорошо по сравнению с ctypes

Нужно было прикрутить библиотеку к ЛИСПу на винде. Взял Clozure CL. Использовать его FFI на винде как-то не получилось, долго разбираться. Пытался поставить CFFI через ADSF-Install, но ASDF-Install на винде тоже работать не захотел :( Ну я понимаю, что, помучавшись, можно все сделать, но я плюнул, запустил питон, оказалось, что тоже вполне юзабелен из Emacs'а (я к Emacs'у фактически начал привыкать после SLIME'а, до этого всегда и везде был vim). Ну и через минут десять я эту библиотеку из питона уже юзал. И только где-то через час-другой стало нехватать лиспа. Вобщем, сделайте кто-то нормальный LISP environment под виндой с FFI, очень надо!

среда, 13 мая 2009 г.

Зачем нужен ЛИСП C программисту?

Например, что вы делаете, когда вам нужно научитья пользоваться программой? Читаете документацию? Или просто запускаете и смотрите что и как? Скорее всего и то и другое. А что если нужно научиться пользоваться новой для вас библиотекой? Документация - замечательно. А как запустить? Если у нас есть DLL'ка, ее нужно сначала загрузить в адресное пространоство процесса, а затем уметь дергать функции в этой DLL'ке с правильной calling convention, передавать правильные аргументы и правильно интерпретировать значания. Кажется, без написания тестовых программ никак.

Но я уже достаточно давно использую для похожих целей python. В начале, я попробовал (на C++) Boost.python и Python C API - хорошо, но можно потеряться в темплейтах если что-то вдруг не заработает c Boost.Python. Затем, юзал SWIG. Тоже ничего, но там такое надо бывает написать в .i файле, придумывать какие-то typemaps для чуть более чем тривиальных случаев. Не хочется такое повторять. Boost.Python и SWIG плохи тем что интерфейс надо оборачивать и писать какой-то код. Быстро поэкспериментировать не получится.

Ничего особо писать не нужно в ctypes, но как-то сложилось, что я его не слишком использовал. А зря.

Сейчас обнаружил, что все варианты FFI в Сommon LISP чем-то похожи на ctypes. Итак, если разобраться в FFI в LISP, что же мы получаем? У нас есть некий процесс, в пространство которого мы можем загрузить любую DLL'ку, подергать ее. При этому это все делается в REPL, плюс под рукой есть мощный язык с помощью которого можно всем этим рулить, прототипировать, экспериментировать. Python с ctypes тоже тут неплох, просто в SLIME получше среда, да и макросами можно весь FFI быстро обернуть. См ранее об libgcrpyt

Имею кучу гемора с установкой чего-то нормального лиспового на винде

Чаще всего рекомендуют CLISP, якобы на винде работающий нормально. Мне, правда больше по душе Clozure, и вроде встало нормально, SLIME запустился. Сначала не появлялся REPL отдельным окном, но потом прочилал в доке, что теперь REPL'а по умолчанию нет (!), и надо специально попросить, сказав в Emacs'е (slime-setup '(slime-repl)). В Clozure CL интересный FFI, будем смотреть сейчас.

среда, 6 мая 2009 г.

О полезности рестартов для долгих вычислений

Я не сильно пока вникал в conditions/restarts, ну то есть прочитал, и все. Пока использую высокий уровень: error, warn, ignore-errors, unwind-protect. Но что классно: запускаешь длинную-предлинную задачу (сейчас пишу нечто, что обрабатывает файлики размером по 12-15G) потом всегда можешь нажать Ctrl-C, и после этого нажать нолик и продолжить с того места где прервал! При этом можно посмотреть как себя задача чувствует, какой прогресс, можно поправить чего-нибудь, вобщем, красота! Можно даже придумать такое извращение, как периодчески слать сигнал процессу затем, чтобы рисовать прогресс бар :)

На C получилось 3500 строк

И то потому что сделал там свои списки и intern, плюс автоматическую чистку ресурсов, с возможностью при ошибке тупо делать longjump. Все это сократило количество кода.

среда, 1 апреля 2009 г.

Сделал на ЛИСПе прототип кода, который надо написать на C

Прототип вместе с юнит тестами потянул на 1100 строчек, интересно сколько эквивалентного C-шного кода получится в итоге?

CFFI и немного простых макросов

Это просто песня. Немного похож на питонячий ctypes, но ctypes отдыхает. С макросами with-foreign-object и пр, которые автоматически создают/удаляют временные объекты, да еще навернув несколько своих макросов сверху, получается круто!

Так, мне нужно использовать libgcrypt из Common LISP'а.

Небольшой набор нужных мне функций выглядит так:



gcry_error_t gcry_md_open (gcry_md_hd_t *h, int algo, unsigned int flags);

void gcry_md_close (gcry_md_hd_t hd);

void gcry_md_write (gcry_md_hd_t hd, const void *buffer, size_t length);

unsigned char *gcry_md_read (gcry_md_hd_t hd, int algo);

const char *gcry_strerror (gcry_error_t err);

const char *gcry_strsource (gcry_error_t err);



Есть и другие, но для примера достаточно.

Лисповые обертки практически изоморфны C-шным прототипам, и пишутся моментально, думаю SWIG тут будет блистать, хотя не проверял и не совсем уверен. Я тут немного хитрю и ставлю тип :pointer для всех типов, о которых я точно знаю, что это указатель. По честному надо было бы сделать typedef'ы, тоже поддерживаемые CFFI.


(defcfun ("gcry_md_open" gcry-md-open) gcry-error-t
"open message disgest handle"
(handle :pointer :pointer)
(algo :int)
(flags :unsigned-int))

(defcfun ("gcry_md_close" gcry-md-close) :void
"close message digest handle"
(handle :pointer))

(defcfun ("gcry_strerror" gcry-strerror) :string
(err gcry-error-t))

(defcfun ("gcry_strsource" gcry-strsource) :string
(err gcry-error-t))

(defcfun ("gcry_md_write" gcry-md-write) :void
(handle :pointer)
(buf :string)
(len size-t))

(defcfun ("gcry_md_read" gcry-md-read) :pointer
(handle :pointer)
(algo :int))



Теперь мы получили интерфейс идентичный С-шному. Без него вообще можно было бы обойтись, и везде использовать макрос foreign-funcall, позволяющий вызвать любую функцию по символу в разделяемой библиотеке с аргументами любых типов. Но я решил их написать чтобы задокументировать интерфейс как он выглядит в ЛИСПе и сделать дальшейший код был более понятным. В разных вариантах FFI для Python'а постоянной проблемой были output параметры. Например, gcry_md_open возвращает результат в виде указателя, которых сохраняется в параметре handle (так что handle это фактически двойной указатель). В SWIG'е мне пришлось бы делать странные аннотации, а также читать довольно много примеров как это делается. В CFFI - красота. На помощь приходит макрос CFFI with-foreign-object:

(defun md-open (algo)
(with-foreign-object (handle :pointer)
(check-gcry-error (gcry-md-open handle algo 0)
(mem-ref handle :pointer))))


CFFI выделяет память под один объект типа :pointer, и связывает указатель на эту память с переменной handle. Память CFFI старается выделить на стеке. Дальше вызывается gcry-md-open с правильным указателем в качестве параметра. Макрос check-gcry-error проверяет код ошибки libgcrypt, если 0 то выаолняет вторую форму и возвращает ее значение, если нет, то возвращает nil, и информацию об ошибках. Вот его определение:


(defmacro check-gcry-error (call-form &optional result-form)
`(let ((err ,call-form))
(if (zerop err)
,result-form
(values nil (cons (gcry-strerror err)
(gcry-strsource err))))))



Итак, уже можно более менее по лисповски открывать handle. Раз мы уже так обернули gcry-md-open, то для симметрии можно уже и gcry-md-close обернуть. Кроме того, я обернул gcry-md-read и gcry-md-write чтобы в эти функции запихивать векторы. И еще: handle нужно закрывать в любом случае, что бы ни произошло, и тут вся красота макросов в стиле with- ЛИСПа (см. with-hash-algorithm).


(defun md-close (handle)
(gcry-md-close handle))


(defun md-write (h v)
(let ((len (length v)))
(with-foreign-object (data :unsigned-char len)
(loop for i from 0 below len
do (setf (mem-aref data :unsigned-char i) (aref v i)))
(gcry-md-write h data len))))

(defun md-read (h algo)
(let* ((c-digest (gcry-md-read h algo))
(len 20) ; cheating - 20 bytes is only for SHA-1, I don't need
; anything else, but generally it's a bug
(v (make-array len :element-type '(unsigned-byte 8))))
(progn
(loop for i from 0 below len
do (setf (aref v i) (mem-aref c-digest :unsigned-char i)))
v)))

(defmacro with-hash-algorithm (h algo &body body)
`(multiple-value-bind (,h err) (md-open ,algo)
(if ,h
(unwind-protect (progn ,@body (md-read ,h ,algo))
(md-close ,h))
(error "error: ~a ~a" (car err) (cdr err)))))


Теперь, если я хочу посчитать хеш нескольких кусков данных, я пишу примерно так (кусок кода, проверяющего PGP подпись):


(with-hash-algorithm h hash-alg
(md-write h #(#x99))
(md-write h (num->2octet (1+ (packet-len whole-packet))))
(md-write h #(4))
(md-write-packet h whole-packet)
(md-write h #(#xB4))
(md-write h (num->4octet (packet-len user-id-packet)))
(md-write-packet h user-id-packet)
(md-write-packet h hashed-data)
(md-write h (vector #x04 #xFF))
(md-write h (num->4octet hashed-data-len)))


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

среда, 18 марта 2009 г.

Решил пободаться с Hunchentoot'ом

У меня на моем макбуке стоит SBCL, LispWorks Personal, Allegro CL Express, Clozure CL.
Hunchentool решил ставить на SBCL, так как все связанное с ASDF там уже идет в комплекте и настроено. Кажется Hunchentoot нормально не работает из-за того, что SBCL собран без поддержки нитей (из macports). Пробую теперь Clozure CL - там еще надо разобраться с ASDF.

Вообще обилие реализаций Common Lisp не раздражает, как в Scheme, где они все совешенно не похожи друг на друга, а даже наоборот - есть из чего выбирать.

понедельник, 16 марта 2009 г.

Пакеты, файлы, зависимости

Понятно, что пакеты как просто namespace не связанные прямо с файлами исходников - это по-лисповски так и должно быть. Вот только не понятно пока, что я как программист от этого выигрываю?

суббота, 7 марта 2009 г.

Мда, не всегда все просто

Python 3.0:

with open("/tmp/file.txt") as f:
for line in f:
print(line)


Common LISP:

(with-open-file (f #p"/tmp/file.txt")
(loop for line = (read-line f nil)
while line
do (print line)))

CLOS и окошки!

Читаю Keene S.E. Object Oriented Programming in Common Lisp. Как же надоело что во всех книжках по ООП в качестве примеров - окна! Напоминает времена когда каждый студент считал своим долгом написать под DOS'ом свою оконную систему.

вторник, 3 марта 2009 г.

AllegroCache

Одна из причин нового всплеска моего интереса к ЛИСПу - это существование такой штуки как AllegroCache. Писал о не длинный пост, но он куда-то пропал и не опубликовался, второй раз облом. Но в кратце - существуют всякие объектные базы данных, но фактически это какой-то нижний уровень, обеспечивающий ACID и базовые операции хранения данных (что-то вроде Berkley DB) с навернутыми сверху механизмами сериализации/десериализации классов в данном конкретном языке. При этом есть еще индексы и прочее. Вобщем - доступ к таким данным - это типичная задача для DSL. Хотя разработчики AllegroCache пошли дальше ии внедрили такой persistence непосредственно в CLOS.

Изучаем ЛИСП

Меня уже довольно давно интересует ЛИСП. Я уже успел прочитать SICP, и даже проработать большинство упражнений. Теперь хочется перейти к Common LISP'у, и отмечать в этом блоге свой прогресс. Цель - писать еженедельно какую-то маленькую программку, хоть задачку какую-то. Очень хочется также применения лиспа к более менее реальным задачам.