Раздел 46 · Effect: Тихая Академия

Босс: обработчик кадров башни

middle-senior~80 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Распечатка Магистрали лежит у Рыдкулла на столе с урока 36. Пустой catch {} и «успешно» после него. За раздел ты разобрал её по строчке: у промиса в типе нет ошибки, ошибку можно назвать по тегу, кроме ошибок бывают дефекты, а причина провала знает обо всех сразу. Сегодня ночью башня T-1 пройдёт ту же ночь, что и Магистраль, и запишет всё, что Магистраль проглотила бы.

Что нужно башне

Проверяется всё, что башня умеет с первого этапа: тесты накопительные. Сверху две вещи в src/tower.ts.

  • processNight(tower, lines), ночь целиком. Строки разбираются все, ни одна не теряется: это decodeAll из урока 42, и каждая ошибка разбора идёт в отчёт по своему тегу. Из разобранных кадров дубли отбрасываются по ключу keyOf из урока 44, первый кадр с ключом идёт в работу, повтор только считается. Каждый кадр проходит checkFrame и respond, и его исход башня разбирает по причинам, как в уроке 41: успех идёт в счёт исходов, ошибка дела в счёт своего тега, призрак ещё и в список призраков, а дефект это сбой.
  • Ошибок в типе нет. processNight отвечает Effect<NightReport>: каждая ошибка уже записана в отчёт, поэтому падать ночи нечем. Тип это проверяет, и Ринцвальд не сможет «на всякий случай» оставить в E хотя бы одну.
  • Сбой одного кадра не роняет ночь. Зависимость башни может бросить прямо при сборке шага, ещё до того, как квитанция запущена. Такой бросок должен стать дефектом этого кадра, а не всей ночи; для этого шаг собирают внутри Effect.suspend.
  • summary(report), сводка ночи одной строкой для Рыдкулла. Текст дословно в заказе.

Имя Tower.processNight для журнала Гекса, точный вид NightReport и правила счёта описаны в заказе.

Чек-лист перед сдачей

  1. Тесты всех прошлых этапов зелёные.
  2. На ночи из десяти строк сумма обработанных, ошибок, дублей и сбоев равна десяти. Ни одна строка не пропала и ни одна не посчитана дважды.
  3. В errors только те теги, что встретились. Нулей для остальных нет.
  4. Повтор кадра не уходит в checkFrame и не шлёт ответ второй раз.
  5. sent.has, который бросает на одном кадре, даёт один сбой, а остальные кадры ночи обработаны.
  6. Убери Effect.suspend и посмотри, что станет с ночью из пункта 5. Потом верни.
  7. expectTypeOf(processNight).returns ровно Effect.Effect<NightReport>. Если в E что-то осталось, ты где-то не записал ошибку.

Башня, этап 11

Последний заказ раздела: башня, которая проходит ночь сама и утром отдаёт честный отчёт. После него Академии есть что положить рядом с распечаткой Магистрали.

Та же ночь на Магистрали

Пока ты пишешь, Ринцвальд прогоняет ту же ночь через код Магистрали. Ему интересно, чей отчёт окажется зеленее.

declare const relay: (frame: string) => Promise<void>;

export const send = async (frame: string): Promise<string> => {
  try {
    await relay(frame);
  } catch {}
  return 'успешно';
};

Десять строк провода, десять ответов «успешно». Что бы ни сделал relay: принял кадр, отказал, бросил на мусоре, ответил на призрак, отправил чужой кадр по чужому маршруту. Функция не может сказать ничего другого, ответ записан в ней до того, как пришёл первый кадр.

А вот та же ночь через башню, с проводом, который рвётся на кадрах для T-9:

import { Effect } from 'effect';

import { processNight, summary, type Tower } from './tower';

const night = [
  'T-3->T-1|5|Text|%D0%B3%D1%80%D0%BE%D0%B7%D0%B0',
  'T-3->T-1|5|Text|%D0%B3%D1%80%D0%BE%D0%B7%D0%B0',
  'мусор',
  'T-7->T-1|41|Ack|40',
  'T-7->T-1|42|Ack|1',
  'T-3->T-2|6|Ping|',
  'T-9->T-1|7|Ping|',
  'T-3->T-1|8|Lost|3',
  'T-1->T-2|9|Smoke|',
  'T-5->T-1|10|Ping|',
];

const tower: Tower = {
  send: async (line) => {
    if (line.includes('T-9')) throw new Error('обрыв');
  },
  sent: new Set([1, 2]),
};

console.log(summary(await Effect.runPromise(processNight(tower, night))));
обработано 4, ошибок 5, призраков 1, дублей 1, сбоев 0

Пять ошибок, которые Магистраль проглотила бы, и у каждой есть имя: мусор на проводе, неизвестный тег Smoke, призрак, подтвердивший кадр 40, который никто не посылал, кадр для T-2, пришедший к нам, и провод, не принявший ответ для T-9. Плюс дубль, на который башня не ответила второй раз.

Сюжетный квест

Это первая из трёх улик квеста «Проглоченная ошибка Гилта». Вторая ждёт в боссе ретранслятора, третья в боссе честного Почтамта. Найдёшь все три, и на дашборде квест раскроется: кредиты и сундук.

Практика

Итог

Утром Рыдкулл читает сводку дважды. Потом достаёт распечатку Магистрали и кладёт отчёт башни рядом, строка к строке: десять «успешно» слева, пять ошибок по именам справа.

Обе бумаги Рыдкулл убирает в одну папку. Улика закрыта: ошибки на Магистрали не исчезали, их прятали, и теперь это видно на одной ночи. Ранга за этот раздел нет. Рыдкулл говорит, что магистр получается не за одну ночь.

Дальше

Рыдкулл перечитывает код башни и загибает пальцы.

У двери стоит Сундук. Он ходит за тобой с урока 02 и в подвале Гекса уже показал, что умеет носить то, что просят свитки. Сундук лязгает крышкой, но не открывается: того, что башне нужно, внутри пока нет.

Следующий раздел курса это EF5 «Сундук»: сервисы, слои и то, как башня перестаёт таскать зависимости руками. Начинается он с урока 47.