thedeemon: (Default)
[personal profile] thedeemon
Когда делаю для себя на Окамле что-то интерактивное, то обычно делаю веб-интерфейс. Нашел в интернетах простенький веб-сервер в 200 строк (из них половина - перечисление кодов ошибок HTTP) - thumper, портировал под винду (реализовав нехватавшую функцию из модуля Unix), дописал разбор параметров и POST запросов. Устроено там все было очень просто и по-функциональному: для нужных путей регистрируются обработчики, являющиеся вполне себе чистыми функциями - параметры запроса на входе, заголовки и тело ответа на выходе. Этого вполне хватало, но вот пришел день, когда понадобилось обрабатывать много данных и выводить прогресс выполнения. И тут всплыл закон дырявых абстракций.
Как говорят классики, чистое функциональное программирование - это программирование посредством математических функций. Такие функции зависят только от своих параметров и описанных выше значений (эту часть обычно забывают упомянуть, кстати) и возвращают одно значение, больше ничего не делая, т.к. делать-то они ничего и не могут - функция есть просто отображение, математическая конструкция. В математике просто нет такого понятия, как время вычисления функции, там если она описана, то считай, что для всех возможных входных значений результат уже известен. В этом чистая математика разительно расходится с компьютерной действительностью, где присутствует еще одно измерение - время, отчего и возникает столько проблем с временем выполнения программ на чистых языках. Но это я отвлекся.
В данном случае я поступил следующим образом. Раньше обработчик запроса возвращал тело ответа в виде строки. Теперь же он возвращал значение типа

type content_t = ContString of string | ContEnum of string Enum.t;; 

Т.е. это либо строка, как раньше, (чтобы старые обработчики сильно не менять), либо ленивая последовательность строк. Ее сервер выводит, делая flush после каждой порции. Долго работающая операция в моем случае меняла состояние программы - загружала из кучи файлов данные, которые использовались в последующих запросах. Получилось, что загрузка данных - это сайд-эффект, а возвращаемое значение - постепенное отображение прогресса загрузки. Выглядело это примерно так:

let files_work_enum = files |> List.enum |> Enum.mapi (fun i fname ->  
    res_list := (parse_file !g_dics fname) :: !res_list; (* time consuming operation *) 
    let percent = (i+1) * 100 / nfiles in 
    Printf.sprintf "<script>setPercent(%d);</script>" percent) in ...

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

Date: 2009-09-22 01:11 pm (UTC)
From: [identity profile] palm-mute.livejournal.com
Можно возвращать результат в виде потока продолжений. Состояние (res_list в данном случае) будет внутри продолжения, потому побочные эффекты не понадобятся.

data Coroutine r a  = Done r | Yield a (() -> Coroutine r a)

Date: 2009-09-23 05:37 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Да, это вариант. Тогда надо будет чуть изменить сервер, чтобы в главном цикле получал новое состояние r и использовал на следующей итерации (при обработке следующего запроса). Спасибо!

Date: 2009-09-22 05:58 pm (UTC)
From: [identity profile] little-arhat.livejournal.com
ссылку или не было! :)
спасибо.

Date: 2009-09-22 09:08 pm (UTC)
From: [identity profile] little-arhat.livejournal.com
проглядел название сервера, простите :)
нагуглил. :)

Date: 2009-09-23 05:32 am (UTC)
From: [identity profile] thedeemon.livejournal.com
Получил ответ по почте:
Не могу ответить в коментах на твой пост 2009-09-22, т. к. у тебя
отключён OpenID, а регистрироваться в ЖЖ не очень хочется.

Возможно, для ленивого формирования ответа серверу достаточно будет
функций модуля Data.ByteString.Lazy? Точно не знаю. В Python это
решается за счёт Iterable(bytes), где Iterable может производить,
например, эффекты чтения при генерации ответов, см. WSGI.

P. S. Нельзя ли как-нибудь более либерально относиться к аутентификации
в блоге? :)


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

Собственно мое решение именно такое, как Вы предлагаете. Окамловский Enum - это аналог Iterable или IEnumerable в других языках. Это работает, потому что разрешены побочные эффекты.

Date: 2009-09-23 10:37 am (UTC)
From: [identity profile] vlasovskikh (from livejournal.com)
Спасибо за решение проблемы с аутентификацией по OpenID.

Profile

thedeemon: (Default)
Dmitry Popov

December 2025

S M T W T F S
 12 3456
789101112 13
14151617181920
21222324252627
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Jan. 27th, 2026 10:20 pm
Powered by Dreamwidth Studios