Еще раз про REPL

luna89

Некоторое время назад я создавал . Я хотел бы еще раз обсудить вопрос, заменяют ли отладички как в джава и C++ полноценный репл.
Рассмотрим для примера простую задачу - веб-фреймворк, в котором можно задавать, какие запросы отображаются на какие функции-обработчики (роутинг). Необходима возможность добавлять функции и редактировать роутинг на лету, без рестарта сервера.
Рассмотрим решение этой задачи на языке с реплом (для простоты будем использовать джаваскрипт). Будем считать, что роутинг хранится в глобальной переменной. Это создает ограничение, что нельзя например стартануть несколько серверов на разных портах, но это ограничение можно обойти в рамках репл-разработки, и пока мы будем рассматривать исходную простую задачу. Итак, у нас есть глобальная переменная, объявленная в файле routing.js:

// file routing.js
routing = {
"GET /api/users/:id" : 'getUsers'
"POST /api/users" : 'createUser'
}

getUsers и createUser - функции объявленные в файле users.js:

function getUsers{
....
}
function createUser{
...
}

При каждом HTTP запросе наш фреймворк лезет в переменную routing и находит там нужную функцию, которую и вызывает.
Если нам надо поменять роутинг и выполнить горячую замену, то мы открываем файл routing.js, редактируем его и жмем Ctrl-C Ctrl-C. Наш текстовый редактор отправляет код в репл (я использую vim-slime для любых реплов, включая bash, psql, nodejs и браузерный джаваскрипт). В репле происходит элементарный eval кода, в результате которого значение переменной routing меняется. Задача решена.
Рассмотрим теперь для примера решение этой задачи на java. Это не критика конкретно джавы, так как нормальную repl-style разработку не позволяют практически никакие мейнстримные платформы, включая современный браузерный javascript и nodejs (объяснение, подробное объяснение). Так вот, в джаве у нас есть дебаггер, который вроде как умеет выполнять код, но для этого надо поставить программу на брейкпоинт. Получается, чтобы заменить роутинг, надо сначала отправить http запрос, поставить где-то брейкпоинт, и выполнить там код. Кроме того, джава дебаггер не умеет добавлять новые методы или менять сигнатуры существующих, что совсем убого.
Кроме дебаггера, есть еще JRebel, который умеет нормально заменять код, но является коммерческим продуктом (это мелочь, но получается что процесс разработки привязан к проприетарщине) и, что более важно, он упирается в джава фреймворки, для которых обычный режим работы - считать конфиги на старте, без возможности реконфигурации в процессе работы. JRebel решает эту проблему за счет того, что он поддерживает ПРАКТИЧЕСКИ ВСЕ джава-фреймворки (реально, многие десятки) и знает, что надо внутри них поковырять, чтобы они перезагрузили свою конфигурацию. Все это работает костыльно, и существуют изменения, которые перезагрузить невозможно. В целом ситуация бредовая - я могу взять open source фреймворк, но мой опыт разработки будет удручающим - требуется купить проприетарную программу, которая знает об интимных внутренностях этого фреймворка.
Большой проблемой является то, что ситуация не улучшается со временем (есть даже локальные ухудшения и с точки зрения процессов разработки мы фактически застряли в 70-х годах, когда компьютеры выглядели вот так (у них нет даже монитора):

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

ramsit

так как нормальную repl-style разработку не позволяют практически никакие мейнстримные платформы, включая современный браузерный javascript и nodejs
http://github.com/swank-js/swank-js не пробовал?

luna89

http://github.com/swank-js/swank-js не пробовал?
Если говорить о js, то проблема не в инструментальных средствах, а
1)В семантике языка. Например, если кто-то где-то сохранил ссылку на функцию, то эту функцию нельзя обновить. Проблема решается, если немного адаптировать архитектуру приложения и стиль кодирования под репл.
2)Модульные системы (commonjs, amd). Чтобы решить проблему, надо либо отказываться от них, либо адаптировать стиль кодирования.
3)Архитектура библиотек и фреймворков. Самая большая проблема. Если библиотека не была рассчитана на репл-кодирование, то ничего не поделать.
Что касается конкретно swank-js, то я предпочитаю связку vim-slime + crconsole. Crconsole использует chrome remote api и не требует встраивать спец код в веб страницу, в отличие от swank-js.

ramsit

Тащем-та да, repl в js годится больше для отладки, но и это весьма неплохо.
В принципе это просто особенности косячного js, т.к. он изначально не задумывался как язык общего назначения
В нормальных интерактивных языках (cl, python, perl etc) таких проблем как правило нет.

Dasar

без рестарта сервера
В этом есть какая-то реальная необходимость? или это личная прихоть?

kokoc88

В этом есть какая-то реальная необходимость? или это личная прихоть?
Насколько я помню, у тс такой сервер, который перезапускается 5-10 минут.

Dasar

у тс такой сервер, который перезапускается 5-10 минут.
после такого действительно захочется его не перезапускать )

Dasar

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

luna89

Насколько я помню, у тс такой сервер, который перезапускается 5-10 минут.
А какой ты предпочитаешь http сервер?

luna89

Если время перезапуска всего сервера длительное время, то не предпочтительнее ли сделать такой прокси между сервером и отлаживаемым кодом, который позволял бы перезапускать моментально отлаживаемый код без перезапуска всего сервера?
Чем эта прокси будет отличаться от репла, и какие у нее будут преимущества? Вот как ты напишешь эту прокси для рассматриваемого примера?

Dasar

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

luna89

Из данной проблемы, один выход - не перезапускать сервер, второй выход - сделать так, чтобы рестарт делался моментально.
Имхо, второй вариант - перспективнее.
Давай посмотрим на проблему шире. Можно считать, что когда мы делаем рестарт сервера, то делаем hot reload кода средствами операционной системы на уровне операционной системы. Считай, что мы уже и так все используем REPL, реализованный средствами операционной системы. К сожалению, когда я пишу, например, UI на джаваскрипте, я не могу сказать, что за эту кнопку отвечает один процесс, а за ту кнопку другой, и чтобы они общались через пайп, с возможностью рестартовать эти процессы по отдельности. Поэтому необходима возможность делать то же, что нам позволяет OS, но на уровне отдельных процессов - заменять код, манипулировать состоянием.
Представь, что ты пишешь скрипт, который должен скачать файлы, скомпилировать их, и загрузить на сервер по SSH. Файлы большие, качаются долго, компилятор тоже медленный. Сначала ты пытаешься подобрать набор ключей к wget, чтобы эти файлы скачались правильно. Потом ты пытаешься подобрать набор опций компилятора, чтобы все скомпилировалось. При этом ты пользуешься тем, что файлы уже скачаны и лежат на своих местах, и их не надо качать повторно. Представь теперь, что ты можешь только запускать весь скрипт разом, и после того как он отработает, скачанные файлы не сохраняются - тебе надо заново запускать скрипт целиком, и ждать когда файлы скачаются. Это было бы очень неудобно, но этим занимаются миллионы кодеров каждый день, тратя миллионы человекочасов - даже в этом треде подтверждают. Причина в том, что состояние не сохраняется на уровне процессов, только на уровне OS.
Еще, я считаю, что ожидание в течение одной секунды так же плохо, как ожидание в течение 5 минут. Все, что больше 50-100 мс и превышает порог восприятия, недопустимо.

Dasar

Можно считать, что когда мы делаем рестарт сервера, то делаем hot reload кода средствами операционной системы на уровне операционной системы.
Есть же существенная разница - ОС гарантирует, что состояние процесса после каждой перезагрузки будет одним и тем же, а hot-reload этого не гарантирует.
Представь теперь, что ты можешь только запускать весь скрипт разом, и после того как он отработает, скачанные файлы не сохраняются - тебе надо заново запускать скрипт целиком, и ждать когда файлы скачаются.
Есть еще вариант - вставить использование file-кэша. Которое включается при отладке и отключается при релизе.
Есть, конечно, минус: усложняется код и требуется искать способ по генерации уникальных идентификаторов переживающих перезагрузку.

var html = file-cache("html-1", is-caching, => wget("http://ya.ru"
.html-parse;

Еще, я считаю, что ожидание в течение одной секунды так же плохо, как ожидание в течение 5 минут. Все, что больше 50-100 мс и превышает порог восприятия, недопустимо.
согласен
Оставить комментарий
Имя или ник:
Комментарий: