LINUX.ORG.RU

Возврат lambda из функции

 ,


0

4

В рамках изучения Lisp я решил один небольшой проект веб-приложения сделать на нём (ибо заказчику всё равно какой язык я буду использовать, ему лишь бы работало, да и время у меня есть).

В качестве веб-сервера использую модуль Hunchentoot. Поскольку мне не нужно ничего супер-сложного, использую его фреймворк easy-handlers для задания маршрутов.

Хочу сделать так, чтобы обработчик вызывался для любого запроса, начинающегося с «/foo/». То есть сюда подходят всякие варианты «/foo/bar», «/foo/baz» и т. д.

У функции define-easy-handler параметр :url может быть не только строкой, но и функцией.

Таким образом можно написать что-то вроде:

(defun starts-with (substr str)
  (if (>= (length str) (length substr))
    (equal substr (subseq str 0 (length substr)))))

(define-easy-handler (foo-page :uri #'(lambda (request) (starts-with "/foo/" (request-uri request)))) ()
  ...)

Но это же не красиво! Хочу вынести создание лябмды в отдельную функцию. Чтобы писать как-то так:

(defun prefix-uri (prefix)
  `(function (lambda (request) (starts-with ,prefix (request-uri request)))))

(define-easy-handler (foo-page :url (prefix-uri "/foo/"))
  ...)

Однако как бы я не расставлял квотирование, либо получаю ошибку компиляции, либо сервер выдаёт 404, либо при обращении к странице вылетает ошибка 500, а в логах пишется, мол мне требуется function symbol, а ты подсунул мне какую-то фигню вместо него.

Как правильно такое реализовать?

★★★★★

(defun prefix-uri (prefix)
  (lambda (request) (starts-with prefix (request-uri request))))
monk ★★★★★ ()
Последнее исправление: monk (всего исправлений: 1)
(defmacro prefix-uri (prefix)
  `(lambda (request) (starts-with ,prefix (request-uri request))))
Bad_ptr ★★★★ ()

Для справки. знаешь как это делается в nodejs?

if(/$\/foo\/.test(request.url)) ...

Это к вопросу обоснованности использования великого и могучего

portquest2016 ()

Лишь бы следующий мэйнтейнер сайта Заказчика, которому «всё равно какой язык», горел таким же желанием изучать Common Lisp, как и OP :-)

anonymous ()
Ответ на: комментарий от anonymous

+++++ фактически, ТС обманывает заказчика, пользуется неосведомленностью. Все равно что продать тачку, к которой не найдешь ни запчастей ни сервисов:)

совесть иметь надо:)

portquest2016 ()

Возврат lambda из функции

Как правильно такое реализовать?

В рамках изучения Lisp

ибо *заказчику*

Тело KivApple было найдено на пустыре с торчащим из-под ребра томиком SICP.

GoodRiddance ()
Ответ на: комментарий от portquest2016

Ну так я не кучу if пишу в одной функции, библиотека, реализующая сервер, предоставляет возможность объявлять маршруты. И, кстати, нормальные JavaScript-фреймворки тоже так построены, а не требуют создавать функцию с кучей if на каждый случай.

Только они позволяют, конечно, сразу указать регулярку вместо строки или callback. Но это уже вопрос к библиотеке, которую я использую, а не к языку программирования. Более того, на самом деле hunchentoot умеет такое (create-regex-dispatcher), но эта возможность не такая удобная как define-easy-handler, потому что требуется два действия - сначала создать обработчик, а затем добавить в таблицу, а define-easy-handler делает сразу всё.

KivApple ★★★★★ ()
Ответ на: комментарий от portquest2016

Ты так говоришь, как будто если бы этот проект поручили веб-макаке, который бы написал кучу лапши на PHP, то следующему ментейнеру было бы легче.

А вообще там такой проект и такие требования, что если он взлетит, то его всё равно переписывать (нет, я конечно постараюсь сделать так, чтобы можно было нормально расширить функционал, но 95% кодеров такой бы запас не оставляло), а если нет, то на него просто забьют и никому копаться опять же не придётся. В качестве БД же я использую мейнстримные решения (выбираю между MongoDB или SQLite), поэтому с миграцией данных пользователей проблем не будет.

KivApple ★★★★★ ()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

о чем ты лепечешь? Какая лапша? Этой одной строкой решается вся лапша, которую ты накалякал, можешь вынести ее в отдельную ф-цию, ты бредишь?

portquest2016 ()
Ответ на: комментарий от KivApple

Ты так говоришь, как будто если бы этот проект поручили веб-макаке, который бы написал кучу лапши на PHP, то следующему ментейнеру было бы легче.

Это уже другой вопрос. И да, было бы все равно лучше. Говнокод — категория весьма субъективная, мартышки как раз, часто считают за говнокод хороший код, в котором не могут разобраться.

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

portquest2016 ()
Ответ на: комментарий от portquest2016

Я предполагаю, что обычно на ноде не создают сервер вручную, а использую какой-нибудь фреймворк. Например, express.

Как там будет это выглядеть? Как-то так (могу ошибаться с синтаксисом регулярки):

app.get('/foo/*', function (req, res) {
  ...
});

А на Lisp так:

(define-easy-handler (foo-page :uri (prefix-uri "/foo/")) ()
  ...)

Я вижу всего лишь три отличия:

1) Длиннее имя функции - define-easy-handler вместо app.get. Вопрос стиля, ничто не запрещает давать функциям на Lisp короткие имена и обвинять Lisp в том, что кто-то дал длинное имя нельзя.

2) Есть параметр foo-page, задающий имя для обработчика. Опять же, это особенность фреймворка, что он требует давать обработчикам имена. Будто бы на JavaScript не может быть такого фреймворка.

3) Функция define-easy-handler не умеет принимать регулярное выражение в качестве параметра. Опять же, это вопрос реализации фреймворке. Я даже сказал уже выше - там есть другая функция, которая таки принимает регулярку, но неудобна мне по другой причине.

Если бы мой код выглядел так:

(defroute "/foo/*" ()
  ...)

То чем бы он отличался от JS-кода выше? Правильно - ничем.

KivApple ★★★★★ ()
Ответ на: комментарий от portquest2016

«на яп, который не поддерживается, в случае необходимости модификации и обслуживания будут большие проблемы»

Не то, что бы не поддерживается :-) Просто Common Lisp на сегодняшний день - язык для гиков :-) Использовать надо с осторожностью :-)

anonymous ()
Ответ на: комментарий от portquest2016

Задача у этого проекта - проверить одну идею. Если проверка пройдёт, то сайт в любом случае будет переписан, потому что требования кардинально изменятся (возможно, это буду делать своя я, и тут уже я подумаю над выбором языка). Если проверка не пройдёт (сайт нафиг никому не будет нужен), то его выкинут и больше трогать не будут.

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

Ясное дело, что я буду рассуждать иначе, если моей задачей будет разработка большого и сложного приложения, которое рассчитано работать и обновляться годами. А сейчас это способ убить двух зайцев - и лисп поучить, и проект сделать.

KivApple ★★★★★ ()
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Короче, я так понял,тебе нравится находить проблемы на пустом месте. Зачем использовать инструменты, которые усложняют и утяжеляют код? По поводу имен. Давать короткие имена — это признак дурного стиля в любом ЯП. Лисп на самом деле, в этом один из первых, можно сказать, когда читал SICP, видел кучу сокращений от «отцов». Если в *канонической* книжке такое, чего же ждать от простых кодеров.

portquest2016 ()
Ответ на: комментарий от portquest2016

Короче, я так понял,тебе нравится находить проблемы на пустом месте. Зачем использовать инструменты, которые усложняют и утяжеляют код?

А в NodeJS уже можно по ошибке на странице, чтобы на сервере можно было изменить значение переменной или переопределить функцию без перезапуска сервера? Для отладки очень удобно: можно отлаживать какую-нибудь страницу после десятого ввода от пользователя не проходя весь этот путь для каждого теста и не тратя время на написание синтетических тестов с вагоном предварительных данных. К тому же можно посмотреть что случилось не так даже при получении ошибки от реального пользователя (подключился к консоли, включил дебаг, пользователь нажал reload, можно смотреть что случилось в потрохах сервера).

monk ★★★★★ ()
Ответ на: комментарий от anonymous

Лично для меня главный недостаток Lisp - отсутствие IDE. Платные за сотни долларов не рассматриваем (под все мейнстримные языки есть куча бесплатных + платные есть хотя бы на торрентах). Если их отбросить, то под онтопик остаётся только Emacs. А он тоже весьма специфичен (хотя я в нём потихоньку разбираюсь, но всё же). В результате чтобы освоить новый язык с весьма нестандартным синтаксисом нужно освоить ещё и текстовый редактор с весьма нестандартным управлением.

Опять же, некоторые считают, что новичок должен сразу использовать все фичи Lisp вроде динамической замены кода. А между тем для большинства задач, особенно на начальном этапе, хватит запуска главного файла проекта (который делает load всех остальных) по хоткею.

KivApple ★★★★★ ()
Ответ на: комментарий от monk

Легко,я так и делаю при отладке. eval. Причем, в js он настоящий, юзабельный, можно заэвалить локальный код. Не знаю как CL, но схема так не может

portquest2016 ()
Ответ на: комментарий от KivApple

Лично для меня главный недостаток Lisp - отсутствие IDE.

Тут сегодня рулит Java :-)

Платные за сотни долларов не рассматриваем (под все мейнстримные языки есть куча бесплатных + платные есть хотя бы на торрентах).

Куча есть куча :-) Ты понял, в общем :-)

Если их отбросить, то под онтопик остаётся только Emacs. А он тоже весьма специфичен (хотя я в нём потихоньку разбираюсь, но всё же).

Это полезный опыт :-) Emacs - это лучший редактор на сегодняшний день :-) Особенно, если знать Лисп :-)

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

Получай удовольствие от процесса :-)

Опять же, некоторые считают, что новичок должен сразу использовать все фичи Lisp вроде динамической замены кода.

Тебе не по фиг что считают некоторые? :-) Just hack away :-)

anonymous ()
Ответ на: комментарий от anonymous

ты пришибленный что-ли? схема не может eval в локальном контексте, я тебе показал. твои художества девочкам оставь, неубедительно.

portquest2016 ()
Ответ на: комментарий от anonymous

У тебя eval вне локального контекста, вне конструкции let, вне окружения, в котором x определен, так понятней? Да и вообще, язык с такими мегакостылями проще на помойку выкинуть. Перл хоть ругают за нечитабельность, там оно хоть стоит того.

portquest2016 ()
Ответ на: комментарий от portquest2016

Давай-ка ты бросишь эту пустую демагогию и всё же покажешь мощь своего этого «eval в локальном контексте». А эти дешёвые понты оставишь маленьким девочкам.

... или успокойся и прими наконец таблетки, назначенные тебе твоим же психиатром.

anonymous ()
Ответ на: комментарий от KivApple

Форматирование лиспокода ужасное у тебя

отсутствие IDE

В емаксе SLIME самое крутое из доступных IDE, хотя оно и не совсем в привычном смысле. Посмотри видосы на ютубе, типа https://www.youtube.com/watch?v=_B_4vhsmRRI и отдельно попробуй SLIME inspector

loz ★★★★★ ()
Ответ на: комментарий от portquest2016

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

loz ★★★★★ ()

Пока начинающие лисперы быстро прототипируют, говнокодеры на жс уже продают. Прост вот блямбу ещё нужно починить - и взлетим!

cdshines ★★★★ ()

Долго ты писать проект будешь, если каждый детский вопрос будешь постить на ЛОР.

Virtuos86 ★★★★★ ()

1. В функции starts-with:

Если у специального оператора if одна ветка, лучше использовать when/unless (см. стилгайд).

2. В функции starts-with:

Для работы со строками есть узкоспециализированные команды! Да, алгоритмически там КОШМАР.

3. Все собранное в макрос:

(defmacro def-route (name prefix &body body)
  `(define-easy-handler
       (,name
        :uri #'(lambda (request)
                 (let ((string (request-uri request)))
                   (when (<= (length ,prefix) (length string))
                     (string= ,prefix string :end2 (length ,prefix))))))
       ()  ; define-easy-handler lambda-list
     ,@body))

вызывать

(def-route foo-page "/foo/"
 ...)

---

Посмотри restas или caveman

anonymous ()
Ответ на: комментарий от anonymous

Если у специального оператора if одна ветка, лучше использовать when/unless (см. стилгайд).

Кому как :-) Например, Джон Фодераро так не считает :-) Это такой же малозначимый совет, как «используйте setf вместо setq» или «не используйте rplacd» :-) Нет никаких гайдов :-)

anonymous ()
Ответ на: комментарий от portquest2016

В жс можно любую функцию забиндить к произвольному контексту.

Но ты евалишь не функцию, а текст.

loz ★★★★★ ()
Ответ на: комментарий от portquest2016

Легко,я так и делаю при отладке. eval. Причем, в js он настоящий, юзабельный, можно заэвалить локальный код. Не знаю как CL, но схема так не может

В SBCL можно сделать eval, работающий с лексическим окружением. Непортабельность заключается в том, что надо работать с объектом, хранящим это самое окружение, а про него стандарт говорит только что он есть, и никаких его полей не описывает. Вот код: простой сервер на лиспе (комментарий)

Jini ★★ ()
Ответ на: комментарий от Jini

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

Безотносительно стандарат есть introspect-environment и hu.dwim.walker которые умеет с этими полями работать переносимо в разных реализацях.

antares0 ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.