Веб-разработка

индекс
236,88

континуации и stateful веб-программирование (Updated!)

Идея совсем не нова. Идея древна.
Однако большинство наблюдаемых вокруг веб-фреймворков упорно игнорируют эту идею.

Она заключается в том, чтобы использовать континуации (continuations) для магического превращения RESTful (stateless) веб-приложений в более удобный и привычный stateful формат.

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

Континуация же — это состояние выполнения приложения на уровне языка.
В scheme он поддерживаются как объекты первого класса — их можно возвращать из функций, передавать как параметры функций, и наконец — вызывать как функции.
При вызове континуации, приложение возвращается в то место, где продолжение было создано, и продложает выполняться оттуда.
Причём это можно делать не один раз.
В некотором роде — это исключения, но действующие наоборот.
Исключение возникает в какой-то точке программы и передаёт управление кудато вовне — там где установлен обработчик.
Продолжение возникает в какой-то точке программы и позволяет передать управление обратно в эту точку. Программа в этом месте будет считать что так и было.

Например, в питоне это реализовано оператором yield.
Функция, содержащая yield считается «генератором итераторов», её выполнение прекращается на первом операторе yield, а функция возвращает объект типа итератор, к которому можно применять next() для вызова следующей итерации, и send(значения) для возврата значения в точку перехода по yield.
Вызов итератора методом next() и send() возвращает очередное, следующее, значение.
Итераторы можно использовать для итерирования чисел (и это может быть безумно весело, если числа, скажем — фиббоначи, и их — бесконечное количество), можно делать обход дерева, можно итерировать всё и вся.
Точно также итератор может генерить не числа или узлы дерева, а web-страницы.

Например, функции для просмотра ресурсов и засовывания их в корзину покупок могут выглядеть так:
browse_stuff():
    # вывести в браузер форму ввода критериев поиска и сохранить введённое в объект 
    criteria = yield(ask_criteria())
    # поиск ресурсов по критериям
    results = find_stuff(criteria)
    # вывести в браузер список ресурсов, листаемый по страницам и чего-нибыдь там ещё
    yield (list_stuff(results))
    # прекратить это гнусное дело
    raise StopIteration()

def buy_stuff(continuation, item)
    if not user.authentificated:
	# переход на страницу запроса пароля, восстановления логина, регистрации
        user = yield(login_form(user))
    # теперь пользователь залогинен, либо создан новый, и можно продолжать
    # спросить, сколько
    quantity = yield(ask_quantity())
    # спросить, куда доставлять
    delivery = yield(ask_delivery())
    # попросить денег
    money = yield(ask_payment())
    # оформить заказ
    status = launch_order(item,quantity,delivery,money)
    # показать страницу с кнопочкой "вернуться"
    yield(message("спасибо за покупку. ждите курьера."))
    # вернуться туда, где была вызвана функция (например в browse_stuff)
    continuation.next()
    # интересно, оптимизирует ли питон такую "хвостовую рекурсию" ?

Параметр continuation — это состояние приложения (выполнения другой функции) в том месте где был совершён переход по ссылке «купить хрень».

Основное приложение при таком раскладе выглядит как итератор.
cont = browse_stuff_iter()
Объект cont сохраняется в сессии, или где-нибудь ещё, и восстанавливается при каждом обращении клиента.
Или же сохраняется несколько объектов, и их идентификаторы кодируются в урлах, засунутых в скрытые поля форм, так, что при нажажатии кнопки «back» пользователь не просто видит предыдущую страницу, а реально возвращается в предыдущее состояние приложения.

На запрос GET вызывается cont.next()
На запрос POST вызывается cont.send(данные формы)
Результаты этих вызовов отображаются как страницы.
Такой метод позволяет генерить цепочку форм как в buy_stuff()
При переходе по новой ссылке, это расценивается как прерывание и текущее состояние передаётся обработчику: buy_stuff(cont,item)

Upd:
Стоит заметить, что ни в питоне, ни темболее в пхп, ни в жаве, нет полноценной поддержки продолжений, на уровне объектов первого класса.
Питоновский yield ведёт себя очень похоже и был использован для иллюстрации идеи.
Как оно будет работать в действителности — не совсем понятно.

Полная поддержка континуаций есть в разных экзотических языках, но и в Ruby в том числе.
Существует несколько серверов с поддержкой продолжений для scheme, lisp, smalltalk, OCaml и JavaScript.
Где-то в комментах затерялась ссылка на простой эмулятор продложений для PHP.


Будущее веба, однозначно — за языком, поддерживающих полноценные континуации!

Особенно если вспомнить, что в языке, поддерживающим продолжения как объекты первого класса, никакие другие конструкции, по большому счёту, вообще не нужны (включая даже конструкции структурирования данных, инкапсуляции и наследования) — они все реализуются через континуации (и как частный случай — функциональные замыкания))

Ну и, как это обычно бывает, подобные мысли уже высказывались более структурированно и посоледовательно :)

Список неиспользованной литературы:


мега-UPD: иллюстрация на scheme
Не ручаюсь за правильность кода. но скобки сбалансированы :)
;; server code
(define (uri->cont uri)  "выуживает откуда-то продолжение по его URI" )
(define (cont->uri cont) "сохраняет куда-то продолжение и формирует для него URI )

(define (insert-uri tmpl uri) "вставляет ссылку в шаблон" )
(define (render-page tmpl args) "рендерит страницу в html" )

(define (handle-request request) "вызывает продолжение, передавая ему request"
  (if (eqv? (get-uri request) init-uri)) ; если запрос на стартовый URI
      (start) ; вызвать старт
      ((uri->cont (get-uri request)) request))) ; иначе - продолжение

(define (make-response cont template) "template: страница со ссылками или форма."
  (render-page (insert-uri template (cont->uri cont))))
;; end of server code

;; application code
(define (quest room)
  (define (parse-request request) "парсит данные формы или чонить и возвращает choice" )
  (define (walk-on choice) "выбрает новую комнату по текущей и choice" )
  (define (get-page) "возвращает html-шаблон для текущей комнаты" )
  (define (response cc) (make-response cc (get-page)) "тупо карринг параметра" )
  (quest (walk-on (parse-request (call/cc response)))))

;; initialization code
(define init-uri "/mytextquest")
(define (start) (quest 'start-room))

Что тут происходит (должно происходить):
0. quest вызывается с парамтером идентифицирующим комнату.
0.5 единственный вызов — последний, самый крайний параметр — выражение (call/cc (response))
1. call/cc (этотакая специальная конструкция, которая) вызывает функцию (response continuation)
2. та в свою очередь — вызывает (make-response continuation template)
3. make-response вставляет в шаблон (например в поле action, или в ссылки перехода) URI идентифицирующий это продолжение.
4. страница рендерится
5. юзер кликает по одной из ссылок на странице или субмитит форму
6. handle-request выуживает по ссылке продолжение
7. и вызывает его с параметром request
8. выполнение продолжается с того места, где стоит call/cc. значение request подставляется в качестве результата вызова call/cc
9. request парсится и мы получаем логику чего там тыкнул юзер
10. walk-on вычисляет в какую комнату попадёт теперь юзер
11. quest рекурсивно вызывается с параметром, идентифицирующим следующую комнату

Если кроме комнаты на выбор пути влияет карма юзера. содержимое карманов. и состояние окружающей экологии — всё это инкапсулируется в 'room'.
Если юзер нажал 'back' и перешёл на предыдущий URI, выдёргивается предыдущее состояние и он реально попадает в предыдущую комнату, все пострадавшие при переходе животные оживают, итп.
+30
25 января 2009, 18:13
44

комментарии (95)

НЛО прилетело и опубликовало эту надпись здесь
+1
chiaroscuro #
> Какие проблемы будет легче решить используя statefull?

Все, в которых есть понятие «состояния».

Яркие примеры: авторизация и сессии.
+4
qmax #
сессия — это всё-таки средство, а не проблема :)
+1
qmax #
Более удобный для императивно- и функционально- ориентированных разработчиков, для которых привычнее описывать логику работы приложения путём декомпозиции на отдельные функциональные точки и программированием каждой точки в отдельном блоке (функции, модуле, объекте), а не развермишеливая её по REST-ориентированным запросам.
Разумеется, для веб разработчика, уже набившего руку на запрос-ответах, это непривычно. Поэтому и стоит тэг «новый взгляд».

Приемущества:
более последовательное описание логики,
отсутствие необходмости заботиться о сохранении состояния,
отсутствие проблемы кнопки «back» и «clone» (иллюстрировано картинками в «Inverting back the inversion»).

Если эти приемущества не очевидны, то можно сказать так:
Это не поможет решить какие-то принципиально новые проблемы.
Это поможет избежать большинства проблем и задач, возникающих при разработке веб-приложений.
0
wayly #
Уважаемый qmax. Хочется заметить, что для «эмуляции»- достаточно лишь идентификатора сессии.

Остальное — дело техники. OOP в PHP давно позволяет делать __sleep/__wakeup. Для нормального «восстановления» объекта достаточно написать базовый класс и указать обработчик сериализованного объекта.

Для того, чтобы писать полноценные (с точки зрения вымышленной здесь проблемы) приложения — достаточно просто чуток подумать головой и посмотреть в ман по нужной области.

И будущее, кстати, не за языками, а за технологиями. А язык — это вопрос применимости. Сейчас начнут холливарить ведь ;)
+1
qmax #
согласен, что дело в технологиях, а языки — лишь инструменты.
можно и на асемблере написать ООП.

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

в качестве продолжения :) этого коммента я написал в апдейте поста иллюстрацию на scheme, использующую нативное использование продолжения в форме call/cc.
уровень условности реализации, думаю, ясен.

попробуйте переписать аналогичное на PHP с сериализацией объекта.
ну просто для сравнения.
0
wayly #
По вашим словам, так за «иллюстрацию на scheme, использующую нативное использование продолжения в форме call/cc.»
будущее? =))))

Вот интересный вопрос: «наиболее естественно поддерживает». Вот лично вам не все-равно?
Для примера: file_get_contents пыховый или нативно-сишный fopen/fread. Нет file_get_contents — напишем для него враппер.

Вопрос не в том, чтобы «переписать аналогичное». Уверен, что переписать можно что угодно на чем угодно. Просто зачем набиваться на холливары — не совсем ясно =)
0
qmax #
Вопрос не в холиварах, а в том, чтобы сравнить разные подходы.
У меня получилось в одну строчку. И я считаю это нагляднее и проще.
Что получится на php?
+1
Assargin #
> По сути, «сессия» придумана, чтобы хранить состояние выполнения веб-приложения.
> А использование её тупо как набора данных — исключительно из-за отсутсвия в языке полнофункциональных продолжений.

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

Сериализация в PHP может быть использована для этой так называемой «континуации»?
0
qmax #
в принципе, если инкапсулировать всё приложение вообще в какой-нибудь обект, то его сериализация вполне сойдёт за континуацию.

вот тут предлагается какаято библиотека для эмуляции продолжений в пхп.

там какраз класс, от которого можно унаследовать такое приложение.

+1
qmax #
Более удобный для императивно- и функционально- ориентированных разработчиков, для которых привычнее описывать логику работы приложения путём декомпозиции на отдельные функциональные точки и программированием каждой точки в отдельном блоке (функции, модуле, объекте), а не развермишеливая её по REST-ориентированным запросам.
Разумеется, для веб разработчика, уже набившего руку на запрос-ответах, это непривычно. Поэтому и стоит тэг «новый взгляд».

Приемущества:
более последовательное описание логики,
отсутствие необходмости заботиться о сохранении состояния,
отсутствие проблемы кнопки «back» и «clone» (иллюстрировано картинками в «Inverting back the inversion»).

Если эти приемущества не очевидны, то можно сказать так:
Это не поможет решить какие-то принципиально новые проблемы.
Это поможет избежать большинства проблем и задач, возникающих при разработке веб-приложений.
0
priestley #
Спасибо за статью, действительно увлекательная тема. Сижу читаю про seaside, ну и smalltalk конечно — интересно как континуации реализованы на практике. Сразу видно несколько косяков, которые вызывают определенные сложности в UX, но с другой стороны многие вещи решаются проще.

Как относительно новый и интересный подход к разработке WEB приложений — однозначно must для ознакомления.
+2
yrashk #
RESTfull => RESTful
statefull => stateful
0
qmax #
упс!
ain't a native speaker :)
0
jdevelop #
намного лучше рулить состояниями в определенных workflow фреймворках, типа OsWorkflow

продолжения — они несколько для других целей предназначены, и запоминания состояния приложения (Memento) — побочный эффект

IMHO
0
qmax #
да нет. продолжения какраз для того и придуманы, чтобы сохранять состояние.
это основная их функция.
а то, что на них можно делать итераторы, сопрограммы, и прочее — следствие.
0
vdshat #
Спасибо за затронутый вопрос. Буквально намедне пришла в голову мысль: «Почему эту особенность сессии даже толком нигде не описывают?» Единственное. описание в статье несколько сумбурное — затрагивается несколько вопросов, а сводится все к одному.
Что касается поддержки языков точек восстановления, это, конечно, удобное подспорье в решении задачи, но не основополагающее. Например, в распределенной бизнес-транзакции одной поддержки языком явно недостаточно.
0
qmax #
статья сумбурна, потомучто толком сам ещё не разобрался. это в общемто — просто изложение идеи.

а конкретного опыта написания таких приложений пока нет.
0
xonix #
тоже как-то интересовался )
www.xonix.info/2007/08/continuations.html
п.с. таки, генераторы это не совсем продолжения
0
qmax #
конкретно в питоне генераторы реализуются ЧЕРЕЗ продолжения. и поэтому их можно заюзать в этом качестве.

в оргинале статьи был ещё более явный пример на scheme.
но я побоялся ломать мозг ещё и этим :)
+1
xonix #
смотря в каком ) в Stateless — да, а в стандартном питоне нет продолжений, и генераторы там созданы на обычных сях, без каких бы то ни было абстракций )
да и генераторы могут быть сделаны не только на продолжениях но и, например, на потоках (подробнее тут www.python.org/dev/peps/pep-0255/)
а продолжение — гораздо более мощная конструкция

почему это не продолжения, например, как сейчас вспомню свою мысль, например можно вернуть продолжение из нескольких вложенных вызовов функций, а с генераторами так не выйдет.
Ну и пару ссылок, где еще это обсуждается (отличие генераторов от продолжений)
www.hutteman.com/weblog/2005/04/26-224.html
zope.stackless.com/spc-sheets-seoul.ppt
0
xonix #
оговорился, в Stackless
0
qmax #
ну да, естественно, продложения более мощные конструкции.
однако yield всёже обладает некоторыми подходящими свойствами.
хотя я слабо представляю как можно их сделать на «обычных сях».
и интересно посмотреть как оно будет реализовано на PyPy.

да и пост в общем-то не про питон :)
0
xonix #
да как на обычных сях, очень просто, посмотрите, например файлик genobject.h из стандартного дистрибутива питона
подходящими для построения на его основе веб-фреймворка (по принципу продолжений) вроде не обладает, т.е. когда захочется сделать компонентную модель (по типу Seaside), что-то мне подсказывает, что не получится
0
qmax #
кстати сказать, именно эта твоя статья меня и побудила написать заметку :)
я там даже коммент оставил.
0
vorbiz #
Поясните дураку. При применении данной технологии приложение становится подобием конечного\бесконечного автомата? Таким образом мы выводим проблемы возможности\невозможности какого-либо действия из данной точки на принципиально новый уровень? Я прав?
0
qmax #
континуации, в принципе, позволяют реализовать конечный автомат в пределах одной функции. но это одно из применений. (в другой терминологии — «сопрограмма с самой собой»).

но автомат подразумевает набор состояний и набор переходов между ними.
континуации сами по себе это не предполагают. они предполагают только «возврат».
автоматы это всётаки перпендикулярный подход.

в частности, эти подходы никак не пересекаются если подумать о недетерминированных автоматах с нечётким состоянием, котрые на континуациях ну никак не вписываются.
0
vorbiz #
А мне моя идея понравилась :) Что если рассматривать веб приложение как последовательность состояний? Тогда можно будет чётко указать возможные действия пользователя. Повысится секьюрность и простота разработки логики. Напишу-ка плагин для рельсов :)
0
qmax #
ну в принципе не плохая идея :)

но как её применить в этих случаях:
1. юзер нажимает кнопку «back» несколько раз и перепостит форму, которая уже была засубмичена
2. а потом нажимает кнопку вперёд, пропуская одну из форм.
3. юзер делает несколько копий первой формы, и продвигается паралельно по нескольким путям.

в случае с континуациями, восстанавливающимися по параметрам запроса, (спрятанным в форму):
1. приложение возвращается в предыдущее состояние
2. приложение возвращается в следующее состояние, как оно зафиксирвоано в форме
3. приложение двигается параллельными путями, как если бы это были разные юзеры

в случае с континуациями, восстанавливающимися из сессии (всегда последнее для любого запроса):
1. приложение посылает пользователя нафик
2. приложение посылает пользователя нафик
3. приложение посылает пользователя нафик
:))
0
vorbiz #
Посылать — жестоко. По поводу кнопок — изменить состояние приложения. Запрос же идёт в любом случае. По поводу параллельности выполнения одним пользователем — надо подумать плотнее. Могут быть проблемы. Простейший выход — своя сессия для каждой из нитей.
0
ScREW #
Отличная идея. Удачи вам.
Когда я занимался разработкой системного ПО на C++, я использовал фрэймворк который реализовывал подход автоматного программирования. Это было прикольно.
0
vorbiz #
Спасибо. Попробую реализовать, когда буду принадлежать себе.
0
david_mz #
Талию где делать будем с кнопкой Back как поступим?
0
qmax #
см. суб коммент выше,
и в статье «Inverting back the inversion of control» — с картинками.
какраз про кнопку back. и возможные извращения с ней.
0
ScREW #
Вопрос наверное глупый, но меня он волнует:

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

P.S. Статья немного сумбурная, но я получил информацию для размышления.
0
xonix #
Сервер может быть не только апач. Например, это может быть сервер приложений (написанный на python, java или smalltalk) который как и апач никогда не завершается и только и занят тем что обрабатывать запросы пользователей. Этот самый сервер приложений может при желании проксироваться через апач. Именно так обычно работают J2EE-сервера, всякие Django и т.д. =)
На PHP (CGI, mod_*) свет клином не сошелся )
как-то так
0
homm #
Еще видимо как-то через fastCGI может быть реализованно.
0
qmax #
да вот так, по большому счёту, если смотреть с позиции «сессия как состояние» — тут вообще ничо сложного нет — сериализовать вообще весь контекст приложения, и дело с концом.

вопрос в том, как это встраивается в язык программирования.
0
qmax #
питон не поддерживает континуации как объекты первого класса, поэтому как такового механизма нет.
наверно, добавлю это в пост.

вродебы единственный язык, котрый поддерживает продлжения полностью — это scheme.
но пример на scheme в статье ломал бы мозг два раза а не один :)

где-то выше комментом кидал ссылку с эмулятором продолжений для php.
(фактически — сериализация объекта и сохранение его куда угодно)

а цель статьи и была дать информацию к размышлению :)
0
VlK #
вы хотите сказать, что папа-CLisp не поддерживает?
0
qmax #
папа-lisp не поддерживал.
а common-lisp это брат scheme.
причом младший.

см. генеалогию

+1
garex #
HTTP в-принципе stateless.

Будущее за новым протоколом, а не языком.

А так это всё извраты, т.е. хаки, хоть и красивые.
0
qmax #
протокол — да.
но это вовсе не значит, что и приложение должно быть stateless.

если на пальцах:
мы с вами сейчас общаемся тоже по stateless протоколу.
коммент прочитан/коммент написан, и всё.
(сами комменты — это не внутреннее состояние, а его проявления.)
но это не мешает мне отвечать даже в нескольких цепочках.

хотя нет. таки мешает. я не могу ответить одним комментом на несколько сразу и приходится использовать ссылки типа «см. коммент выше» :)
НЛО прилетело и опубликовало эту надпись здесь
0
qmax #
если протокол http заменится каким-либо stateful аналогом, то он вполне сможет обеспечить обратную совместимость.

хотя как-то слабо представляется WWW на отличных от REST-подобных принципов.
+1
vseloved #
Континуации звучит как-то не по-русски. Чем вам не угодили продления?

Кстати, идея использования продлений родилась в Smalltalk фреймворке Seaside.
Сейчас она реализована в разных диалектах Лиспа: в PLT Scheme Web Server и Common Lisp фреймворках UnCommon Web и Weblocks.

По поводу REST и состояний: далеко не вск согласны, что RESTful = stateless (см. blog.dhananjaynene.com/2008/11/rest-fomenting-unrest-is-restfulness-a-semantics-game-why-does-rest-require-statelessness/)
0
xonix #
0
vseloved #
настаиваю на продлении: продолжение звучит слишком неопределенно, как верно подмечено ниже. :)
статью из Википедии в данном случае можно считать лишь мнением ее автора, поскольку термин новый и консенсуса, как его называть, нет.
см., например: lingvo.yandex.ru/en/?text=continuation&from=os
0
xonix #
ну в интернете используют в основном вариант «продолжения», сравните:
1 vs 2
+1
vseloved #
потому что люди берут первый перевод, не задумываясь :)
+1
qmax #
тогда уж «возобновление»

а концепт сам по себе не очень определённый — это и действие, и результат действия, и результат как незадействованный результат действия :)
0
vseloved #
возобновление — это по-английски будет restart, а это уже чуток другая концепция из Common Lisp (функция-реакция на исключительную ситуацию).

«это и действие, и результат действия..» — как раз то, что написано в Лингво про продление
",,, и результат как незадействованный результат действия" — что вы имели в виду? В принципе, результат есть у любого действия, и его не обязательно задействовать
0
qmax #
да. тут есть тонкость, которая затронутав комменте выше.
stateless — это протокол.
в принципе, выражение в статье «RESTful (statles)» не совсем предполагает синонимичность.

если не путаю, по принципам REST состояние приложения абстрагируется как «ресурс» (идентифицируемый URI, потомучто там всё ресурс.
в этом свете, «продолжения» — какраз и есть такой ресурс.

статью сейчас покурю. спасибо.

P.S.
слово «континуации» я скалькировал для придания ему большего оттенка терминологичности.
слово «продолжение» в контексте, не предполагающем понимания этого понятия, будет выглядеть немного неопределённо.
0
qmax #
а Seaside смотреть пока не решаюсь, потомучто совершенно не видел SmallTalkа как такового.
0
vseloved #
Smalltalk — это более consistent Руби ;)
Посмотрите Common Lisp ;))
0
qmax #
common lisp я смотрел в подаче Хювённен Сеппяннен.
но не шибко досконально.

и мне он показался несколько «грубым» :)
по сравнению со scheme.

сейчас я занимаюсь изучениам scheme, и наверно пока не буду отвлекаться на альтернативы. а может, наоборот, будет полезно…
0
vseloved #
это очень плохая подача, гораздо лучше — Practical Common Lisp (есть перевод на русский, если это имеет значение) или On Lisp.
ну, а в целом, как по мне, разобравшись с CL вы будете хорошо понимать Scheme, а вот наоборот не верно, поскольку тема макросов не будет раскрыта
0
qmax #
вроде бы в scheme макросы продвинутее чем в лиспе?
0
vseloved #
Я бы сказал наоброт. Но это, конечно, вопрос философский (по которому, в основном, и разделяются пользователя CL и Scheme. Точнее, те, кто использует CL утверждают, что макросы — это одна из ключевых причин выбора именно его, а Schemer'ы придают им намного меньшее значение).

попробуйте и то, и то, и сами сможете сказать
0
mou #
Не в Seaside родилась идея. Впервые я увидел continuations в Cocoon
0
NaTTs #
Огромное спасибо! в закладки и разбираться в контексте…
0
Silverstorm #
В контексте HTTP протокола вообще не вижу никакого позитива в такой реализации. Какую проблему Вам решить не удается изза stateless? Помоему все проблемы уже давно решены и довольно успешно, все к ним привыкли и они имхо совсем не dirty tricks
+2
qmax #
лёхкое противоречие в вашем комменте.

если «все проблемы уже решены» — это значит они есть.
неважно, насколько успешными решениями они закрыты.
и то, что все привыкли — никак не признак отсутствия проблем как таковых.

континуации же ликвидируют эти проблемы в принципе.
это не очередной способ решения.
это подход, при котором проблем не существует и решать нечего.
0
coolness #
Если хранить состояние в сессии, то ссылку на такую страницу дать не получится!
Для меня это было решающим фактором, чтобы отказаться от этой затеи =)
0
qmax #
состояния можно хранить внутре сервера.
а иденитфицировать урлом.

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

я таки попробую сделать квест.
0
xiWera #
ой скока можно будет переполнений организовывать… :)
0
qmax #
каким образом?
0
xiWera #
ну как же, на сервере надо будет хранить данные необходимые для продолжения континуации :)
0
qmax #
эти данные доступны для заполнения и темболее переполнения настолько же,
насколько разбалансированность скобок в исполняемом скрипте.
0
xiWera #
Попробую выразить свою сумбурную мысль поподробнее.

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

Соответственно простая форма ввода будет выглядеть на языке поддерживающем континуации примерно так:

do{
print_form();
data=read_form();
}while (check_data(data));
print_success();

на самом деле в строчке read_form() веб-программа сохраняет свое состояние, а в следующий запрос этого же пользователя начинает свою работу с присвоеню data значений формы и проверки check_data.

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

0
qmax #
А… так вы об этом.
Я почемуто подумал об уязвимости через переполнение буферов.

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

Furthermore, garbage collection can not be used to free this memory, because there are roots outside the system: users’ browsers, bookmarks, brains, and notebooks.

Поэтому всякие менеджеры континуаций с таймаутами и прочим.

И ещё есть подозрение, что размер будет зависить от структуры кода, а не только от количества параметров.
0
xiWera #
конечно, это не подозрение, надо храниить стек и все переменные на стеке, соответственно информацию о всех откртых ресурсах. А это ОЧЕНЬ накладно. Пример:

$data = sql.exec(«SELECT * from table»); //здесь селект миллиона строк
switch( get_user_action()){

case page_out: print_page($data);

}

сколько потребуется ресурсов сервера чтобы сохранить состояние по выходу в get_user_action()?
0
qmax #
засрать всю память — тривиальнейшая задача на абсолютно любом языке, включая русский.
0
xiWera #
ага, только в веб-языке поддерживающем континуации это необходимое зло :)
0
qmax #
в труъ функциональных языках:
— нет присваивание переменным;
— нет скрытого goto в форме последовательности статментов;
— у функций отсутствуют побочные эффекты;

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

P.S.
а на языках запредельно высокого уровня, транслятор вполне сможет вывести, что вообще весь этот кусок кода — сплошное наебалово, и выполнять его не нужно :))
0
xiWera #
нет, вы не совсем правы:
проблема в том, что при выходе из программы с последующей континуацией, транслятор не знает какие данные потребуются после континуации. В приведенном примере транслятор не знает будет ли дальше использоваться результат sql запроса или нет.
0
qmax #
очевидно даже не глядя в код, что после возврата потребуются следующие или предыдущие 5 строчек.
такое обращение с данными вписывается в интерфейс итератора и реализуется курсором бд.
0
xiWera #
а почему вы не можете представить что-то другое на месте этих sql данных?

$data=read_from_file($f,100000); //read 100k from file
do_some_work_with_data($data);
ask_user()
do_some_work_with_data($data);

вот к примеру, разрыв в точке ask_user(), 100килобайт на ура
0
qmax #
в целом — не вижу ничего плохого в том, что технология делает неэффективным написание говнокода.
0
xiWera #
то есть вы предлагаете использовать континуации без плюшек которые они предлагают? :) зачем тогда нужны континуации? :)
0
qmax #
континуации нужны для сохранения состояния приложения.

а для хранения данных придуманы бызы данных и всякие кэши.
0
xiWera #
разве состояние приложения это не есть:
1) состояние стека
2) состояние перменных
3) открытые ресурсы и их состояние (на каком байте открыт файловый дескриптор, и тп)
0
qmax #
ну вот как показывает опыт эрланга —
переманных и стэка вообще может не быть.
0
xiWera #
может не быть, но при этом и состояния как такового нет :)

подумайте как вы можете отделить одного пользователя от другого (одно состояние от другого) если у вас они не отличаются :)
0
Kirax #
Спасибо за инфу! Всегда считал что общепринятый подход писать полезный код в «контроллерах» запросов это извращение!

Есть правда одна проблема — в динамических языках далеко не все объекты поддаются сериализации… В python-е приходится писать обработчики set_state/ get_state для pickle. Но как быть с 3rd party классами? Недавно использовал mechanize и нужно было сохранить состояние, а pickle его не берет. Пришлось выкручиваться руками, а как бы было красиво, если просто сохранять состояние…
0
qmax #
эта проблема связана со путаницей между состоянием приложения и состоянием объектов.
если состояние приложения полностью реализуется ограниченным (в пространстве имён) набором переменных — то сессия целиком и полностью решает все возможные проблемы.

континуация же не ограничивается сохранением значений переменных (хотя это и не особо видно в псевдо-примерах) — это сохранение состояние вычисления.
к значениям переменых добавляется ещё и место «прерывания».

различие сложно представить, имея в голове лишь опыт процедурного программирования с использованием традиционных техник.

надо пробовать.
оставайтесь на нашей волне :)
0
lig #
идея понятна, но кто угодно может что угодно городить в теории, пока мы будем хуярить на имеющихся у нас инструментах
0
mou #
Продукт созданный с использованием продолжений, пока вы, извините, х****е на своих инструментах
–1
lig #
Ну и на лиспе делают сайты и не плохие.

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

Я про то, что технологию должен поддерживать инструментарий, чтобы можно было работать на высоком абстрактном уровне.
0
mou #
Ну я своим комментарием, хотел сказать, что инструменты уже есть.
0
qmax #
а сайты на лиспе где видали?
–1
lig #
за 5 минут в гугле нашел пару десятков.
ищущий да обрящет…
+1
mou #
Стоило взять пример из Seaside для более наглядной демонстрации.
go
| user |
user := self call: AuthRegisterComponent new.
user username ~= ''
ifTrue: [ self inform: ('Good Job ', user username) ]
ifFalse: [ self inform: 'You did not register' ]

Пока выполнятся эти строки, произойдет 4 HTTP запроса.
А еще посмотрите на это в действии DabbleDB
0
qmax #
в действии смотреть не особо интересно.
интересней, что у него унутре.
0
qmax #
но за ссылку спасибо.

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