Как ворчун попробовал language server и приблизился к мечте лисп-программиста

A curmudgeon tries a language server

Как ворчун попробовал language server и приблизился к мечте лисп-программиста

Автор, привыкший писать код «по старинке» — редактируя, компилируя и перезапуская программу, — завидует лисп-программистам, которые живут внутри работающего процесса и меняют код на лету. Он решает приблизиться к этому в Haskell, используя language server (hls), ghcid и библиотеку foreign-store. В статье он подробно описывает настройку Emacs, Eglot и Nix, а также делится опытом создания toy-проекта для визуализации дифференциальных уравнений, где при сохранении кода автоматически пересобирается и перезапускается программа с сохранением состояния. Получившийся рабочий процесс позволяет не покидать редактор, хотя и не достигает полной мощи Lisp REPL.

Мы никогда не покидаем наш редактор.
  1. dieggsy

    Как разработчик на Common Lisp, вступление о Лиспе кажется мне в целом (?) правдивым. По моему опыту, мы действительно "компилируем и выполняем" код, просто можем делать это инкрементально и с меньшим переключением контекста из-за описанного рабочего процесса. Но довольно часто всё же есть отдельные шаги компиляции и запуска, просто не на уровне всей программы (например, скомпилировать функцию и запустить её или вызывающую функцию). И несмотря на то, как часто это преподносят как преимущество, переопределение кода во время реального выполнения не так уж распространено, я думаю. Да, иногда это делается, но многие программы в принципе не подходят под эту парадигму. Это может хорошо работать, например, для некоторых игр или долго работающих серверов. Что касается работы в образе без исходного кода: это кажется ужасной идеей. Можно делать так для тривиальных разовых экспериментов, но в целом полезно записывать и организовывать код заранее. Я даже не знаю особенно простого или чистого способа выгрузить исходный код из образа, как описано. Это можно сделать, но все способы, которые я могу придумать, звучат как боль или беспорядок. Может, это просто мой опыт, или я упускаю какую-то удобную фичу коммерческого Lisp или что-то в этом роде.

  2. cassepipe

    Не совсем то же самое, но стоит упомянуть для C/C++ программистов: gdb на самом деле может работать как своего рода "дешёвая" живая среда для C и C++. Когда вы остановились на точке останова, вы можете вычислять C/C++ выражения, изменять состояние, вызывать функции и, с помощью compile code, заставить GDB компилировать и выполнять новый C/C++ код в контексте работающего процесса:

    (gdb) set variable state->counter = 42

    (gdb) call recompute_state(state)

    (gdb) compile code

    > printf("counter = %d\n", state->counter);

    > do_something(state);

    > end

    Это даёт удивительно приятный опыт "REPL, прикреплённого к работающей C-программе". Вы можете ковыряться в куче, менять переменные, вызывать существующий код и вставлять небольшие кусочки нового кода без перезапуска процесса. Один случай использования, который я вспоминаю с теплотой, — реализация различных структур деревьев и добавление функции print_dot, которая выводила дерево в формате .dot, и когда я вызывал эту функцию из gdb, окно xdot перерисовывалось с обновлённым графом дерева. Я знаю, что это не совсем то же самое, что образ Lisp, где внедрённый код становится постоянным, но всё равно приятно иметь. P.S.: Если вы впервые пробуете tui gdb и ваша программа всё ещё завалена print-выражениями, вы можете очистить экран после вывода printf с помощью Ctrl+L или команды refresh.

  3. PaulHoule

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

  4. ux266478

    Честно говоря, учитывая степень структурного разрушения, которую вы получаете от GHC, разработка на основе образов звучит как очень плохая идея. Haskell — такой странный и неинтуитивный язык с системной точки зрения, потому что это почти как будто вы ограничены написанием макросов для языка, который вы никогда напрямую не видите и с которым не взаимодействуете. Трансформация от вашего исходного кода к тому, что выдаёт компилятор, может быть крайне нелокальной и непредсказуемой, даже до того, как вы начнёте вводить такие вещи, как пользовательские правила переписывания. По мне, умный способ работать с Haskell — это полная противоположность итеративности. Проработайте это на бумаге, отточите, найдите алгебраические правила для этого, отточите ещё немного, и только потом начинайте писать код, когда точно знаете, что делаете. Я думаю, что поддаться искушению "просто начать писать код" — это путь к непрозрачной типовой лапше. Это всего лишь мои ощущения.

  5. airza

    Я узнал много интересных фактов о Haskell и Lisp из этого поста. Эти факты заставляют меня бежать с криками от попыток использовать любой из них для разработки ПО. Моё время с Haskell сделало меня лучшим программистом, и я готов поспорить, что LLM сделали часть документации более понятной, но...

Ещё за этот день

2026-08-26