пятница, 3 июля 2026 г.

[prog.eiffel] Сохраню в склерозник пример работы с anchored-типами в Eiffel

Есть очень прикольный язык программирования -- Eiffel. А в нем есть очень прикольная фича -- заякоренные типы (anchored types). Давеча пришлось в одном из разговоров про нее вспомнить и набросать небольшой пример, чтобы проверить те или иные предположения.

У Eiffel-я есть он-лайн компилятор (что-то по типу godbolt, wandbox, ideone), но в нем нельзя просто так сохранить сделанное и поделиться публичной ссылкой. Поэтому помещу под кат код написанного примера, просто на память, а то вдруг еще раз потребуется.

В чем суть примера?

Есть базовый класс MESSAGE.

Есть базовый класс ENVELOPE, который хранит в себе MESSAGE. Но класс ENVELOPE написан с использованием MESSAGE в качестве якорного типа. Что позволяет создать наследника SIGNED_ENVELOPE, который хранит уже не просто MESSAGE, а SIGNED_MESSAGE. Но менять унаследованные из ENVELOPE методы не нужно, Eiffel сам разбирается с тем, что в SIGNED_ENVELOPE методы make и change_content получают уже не MESSAGE, а SIGNED_MESSAGE.

При этом Eiffel что-то может проверить в compile-time. Например, если у нас есть ссылка signed_env с типом SIGNED_ENVELOPE, то в вызов signed_env.change_content мы не может отдать просто ссылку на MESSAGE. Будет ошибка компиляции, компилятор ждет от нас SIGNED_MESSAGE (или наследника SIGNED_MESSAGE).

Но если у нас есть env типа ENVELOPE и мы env присвоили signed_env (т.е. теперь env ссылается на экземпляр SIGNED_ENVELOPE), то компилятор пропустит передачу обычного MESSAGE в env.change_content. Но ошибка будет диагностирована в run-time с выбросом исключения. Т.е. "обмануть" Eiffel не получится: то, что Eiffel не смог поймать в compile-time, он поймает в run-time.

среда, 1 июля 2026 г.

[life.cinema] Очередной кинообзор (2026/06)

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

Фильмы

Грязные деньги (In The Grey, 2026). Тупо, прямолинейно, приторно красиво, но бодренько. За неимением лучшего можно посмотреть, чтобы скоротать вечер.

Мандалорец и Грогу (The Mandalorian and Grogu, 2026). Отличная сказочка для семейного просмотра с детьми младшего школьного возраста. Разве что немного мрачноватая. В любом случае кино абсолютно детское, взрослым к нему всерьез относиться не стоит.

Коммерсант (2026). Можно смотреть. Но меня раздражало две вещи: Александр Петров в роли Александра Петрова и клиповый монтаж из очень коротких сцен. Так что фильм хорош рассказанной историей, но исполнение могло бы быть гораздо лучше.

Ограбить Лондон (Fuze, 2025). Ничего не ждал от фильма вообще, думал обычная третьесортная поделка. Оказалось на удивление хорошо. Т.е. это далеко не шедевр, уровнем чуть повыше среднего, но на фоне всего остального шлака, прям глоток свежего воздуха.

Охота за тенью (Shadow's Edge, 2025). Очень бодрая сказочка. Джеки Чану мое почтение.

Мусорщик (Wasteman, 2025). Мне показалось, что это неплохая тюремная драма. Но, во-первых, понятия не имею насколько это достоверно. И, во-вторых, смотрел на скорости 1.25, потому что на нормальной скорости было ну как-то совсем уж нудно. Тем не менее, развязка понравилась.

Сериалы

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

Заложник (первый сезон, 2026). Начало бодрое, затем все скатилось в какую-то сопливую Санта-Барбару. При этом сериальная затянутость ощущается даже не смотря на то, что в сезоне всего шесть серий. Ну и отдельно не понравилось то, что повествование оборвали на полуслове с явным намерением продолжить в следующем сезоне. Снято, однако, качественно. К картинке претензий нет.

Бухта вдов (Widow's Bay, первый сезон, 2026). Скучно, что-то реально интересное и удивляющее происходит очень редко. Сама история толком ничем не закончилась, тут нам явно предлагают ждать следующих сезонов. Купился на хвалебные отзывы в Интернете, по итогу не понравилось, жаль потраченного времени.

Красота (The Beauty, первый сезон, 2026). Первые серии хорошие -- тут и экшен, и драйв, и многообещающая завязка. Затем темп повествования потерялся, драйв улетучился. Финала как такового у первого сезона нет. Создатели обрывают происходящее буквально на полуслове. Возможно, когда выйдет второй сезон, и история достойным образом разовьется, то этот сериал можно будет хоть как-то оценить. Но не в его текущем виде.

Не смог досмотреть

Опасные отношения (Over Your Dead Body, 2026). Это ремейк фильма Поездка (I onde dager, 2021). И, как по мне, так оригинал смотреть интереснее, в нем как-то все более естественно.

Дьявол носит Prada 2 (The Devil Wears Prada 2, 2026). Происходящее на экране невозможно воспринимать серьезно.

Вне категории

Ангелы Ладоги (2026). Такое впечатление, что фильм рассчитан совсем на другое поколение. Возможно, на современных детей 8-10 лет. Как мне его оценивать непонятно.

Литвяк (2026). С одной стороны, хороший военный фильм, без пьяных командиров, трусливых комиссаров и неадекватных СМЕРШев с наганами в руках. С другой стороны, рассказанная история нисколько не зацепила, главные герои показаны так, что не успеваешь к ним прикипеть так, чтобы их гибель заставляла тебя переживать именно потому, что ты успел проникнуться к ним какими-то чувствами. Кроме того, напрягает и раздражает то, насколько много сцен в фильме снято на фоне зеленого экрана. В общем, не знаю, как оценивать. За попытку снять хороший фильм о войне -- отлично, за исполнение -- незачет 🙁

понедельник, 29 июня 2026 г.

[prog.thoughts] Начинаю думать, что бесстековые короутины в C++ следовало делать чуть иначе

Прежде чем перейти к заявленной в заголовке теме небольшое погружение в контекст. Появились небольшие ресурсы для того, чтобы попробовать добавить в SObjectizer поддержку бесстековых короутин из C++20.

Изначально я планировать только позволить пользователю описывать event-handler-ы, в которых можно было бы делать co_await. Но по мере продумывания способов реализации столкнулся с тем, что не имею на руках никаких механизмов запуска внешних по отношению к SO-5 короутин. А это нужно, чтобы проверить, что из event-handler-а можно дернуть, скажем, короутину из Asio, и когда она завершится, управление должным образом вернется event-handler-у.

И тогда появилась мысль сделать сперва возможность асинхронной работы с mchain-ами. Сейчас в SO-5 есть синхронные версии receive и select, а что, если предоставить их асинхронные версии? Тогда в event-handler-е можно было бы написать что-то вроде:

so_5::event_handler_task_t
some_agent::evt_some_handler( mhood_t<some_msg> cmd )
{
  auto result = co_await so_5::async_receive( so_5::from( test_chain ), ... );
  ...
}

Тогда отправляя (или не отправляя) сообщения в тестовый канал я бы мог тестировать поведение event-handler-ов.

Но стоило погрузиться в задачу создания асинхронных версий async_receive и async_select-а, как стало возникать подспудное подозрение, что с C++ными бесстековыми короутинами что-то не так. И я не говорю про их мудреность (это тема отдельного разговора). Было ощущение, что несмотря на заумность и гибкость C++ных короутин в них все-таки чего-то важного не хватает.

А выкристаллизовалось понимание чего же именно не хватает в процессе знакомства с библиотекой Capy (которую сейчас пытаются запихнуть в Boost). Как раз авторы данной библиотеки четко выделили проблему, которую не удавалось нащупать мне. И попробовали ее решение прикрутить сбоку синей изолентой.

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

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

В Capy ее предлагают решать за счет введения специальной сущности -- Executor-а и за счет изменения интерфейса сущности Awaiter-а: метод await_suspend для Awaiter-а вместо одного аргумента получает два:

std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env);

И во втором параметре передаются вещи, которые могут потребоваться короутине (и ее дочерним короутинам): executor, stop_token, allocator.

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

Это как раз то, чего мне не хватало для реализации асинхронных версий receive/select. Я уже сам пришел к мысли о том, что в асинхронный receive нужно передавать какой-то coro_scheduler, который будет отвечать за то, чтобы возобновить приостановленный receive/select именно там, где это разрешено. Например, если receive вызывается из event-handler-а, то это могло бы выглядеть так:

auto result = co_await so_5::receive(
  so_5::from( test_ch ).resume_by( this->so_coro_scheduler_for_this_agent() )...,
  ... );

А если mchain используется вне агентов, то может быть что-то вроде:

so_5::cpp_coro::this_thread_scheduler_t coro_scheduler;
coro_scheduler.sync_wait(
  [&coro_scheduler, &ch]() -> so_5::cpp_coro::async_receive_task_t {
    co_await so_5::receive(
      so_5::from( ch ).resume_by( coro_scheduler )..., ...);
  } );

И каково же было мое удивление, когда знакомясь с библиотекой Capy увидел, что ее авторы тоже пришли к явной передаче executor-а.

Но, повторюсь, и подход Capy, и мои попытки придумать некий условный coro_scheduler для SObjectizer-а -- это попытки прикрутить решение сбоку посредством синей изоленты.

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

Например, чтобы обращение к co_await можно было параметризовать. Скажем, передавать executor/scheduler непосредственно в co_await:

co_await(executor) some_task();

И чтобы этот executor передавался бы параметром в await_resume. Может быть в виде ссылки на некоторый специальный объект environment, как это делается в Capy.

Есть у меня подозрение, что с такой явной передачей executor-ов в co_await, код с короутинами и писать, и читать было бы гораздо проще.

PS. Раз уж заговорил про попытку добавить поддержку короутин в SO-5, то слегка обозначу текущий статус. Пока до чего-то работающего еще далеко. Пытаюсь двигаться от простого к более сложному. Сперва хочу попробовать сделать асинхронную версию receive/select. Чтобы с ее помощью перейти к поддержке event-handler-ов в виде короутин. Затем, возможно, попробую погрузиться еще глубже, чтобы разрешить короутины в роли обработчиков входящих сообщений в receive/select (если это вообще возможно). Работы много, ресурсов мало, движется все очень медленно. Поэтому я сам в течении ближайших пару месяцев никаких значимых результатов не жду.