Спасибо за наводку. Около месяца аптайма компьютера и браузера под кеш нарезервировалось почти все 32ГБ. После большого перерыва стал пересобирать систему, обновление большое, понадобилось оперативной памяти прилично. И вместо того, чтобы откусить от того, что нарезервировалось, система полезла в своп на диск, на 3,5ГиБ примерно. Разумеется всё начало адово фризиться (впрочем не так долго, т.к. SSD, но тем не менее). Отключил к чертям своп на диске и подключил ZRAM swap, чтобы такого больше не повторялось.
Живу в Финляндии, о*уел немного от прочитанного, надо признаться. А ведь я когда-то выбирал между Германией и Финляндией, ну где быстрее нарисуется хороший случай. Повезло же мне! Розовые очки конечно тоже поблекли в нюансах, но никаких серьёзных потрясений за полтора года не было. Окажись я в Вашей шкуре, плевался бы очень долго. Хотя из перечисленного о весьма и весьма специфичной культуре германского сервиса я был наслышан.
Это у Вас молниеносно по памяти не текло. Если там чего-то недохватило, то в фризы могут быть и незначительными. А вот если у вас что-то пошло не так, и нечто начало аллоцировать как не в себя, да ещё параллельно в несколько потоков/процессов (ну вот сидели Вы, погромировали, экспериментировали, допустили страшное недоразумение в творческом потоке), а Вы и не заметили сначала, то если есть своп, пиши «пропало», потому что как оно доест до свопа, всё повиснет, и проще ресетнуть компьютер, чем ждать пока оно мееееееедленно доест своп (а диск медленный) и встретит свою OOM-смерть. А вот если свопа нет совсем, то OOM такой франкенштайн встретит гораздо быстрее.
ОС будет мне благодарна? Ведь оперативной памяти у меня, ух, аж на 3 Electron-приложения хватит, положу swapfile прямо туда, в оперативную память. Зачем мне дёргать лишний раз не вечный и медленный диск?
Когда возникает необходимость в swap-файле? Самый частый ответ — «когда не хватает памяти». И, естественно, это ответ неправильный почти полностью. Правильный ответ — необходимость в использовании swap-файла возникает тогда, когда система не может удержать в памяти необходимый кэш и грязные страницы.
Это прекрасно, когда объем оперативной памяти достаточно велик, чтобы вместить и весь необходимый кэш, и данные. Но если у вас бюджетная система с 8 … 16ГБ оперативной памяти
Взаимоисключающие параграфы? «Памяти хватает, система не бюджетная (32Г думаю достаточно для аргумента), зачем мне своп, если я точно знаю, что если система дошла то того, что полезла в своп, то проще нажать Reset, т.к. фризы будут такие, что даже открыть терминал по хоткею и кильнуть вредный процесс может стать невыполнимой задачей.»
«когда не хватает памяти». И, естественно, это ответ неправильный почти полностью.
В каком месте он тут неправильный? Если:
Это прекрасно, когда объем оперативной памяти достаточно велик, чтобы вместить и весь необходимый кэш, и данные.
Ну я уже всех не помню, это было лет 8-9 назад. В качестве DAW Reaper, из VST плагинов GuitarRig, TRex, эквалайзеры, компрессоры, ревербераторы, набор плагинов Wave, Native Instruments, какой-то очень известный барабанный сэмплер, забыл название, я всех уже и не вспомню, и имена не помню все. Какие-то, куда всякого DRM и прочей «защиты от пользователя» напихали, могут и не заработать без плясок. Но те, что мне были нужны, все работали.
Ну для кого-то персонально и настал. Я в своё время, когда убедился, что нужный мне музыкальный софт (который я позже заменил open source решениями) отлично работает из под Wine, то избавился от дуал бута, и за все эти года (наверное уж с десятилетие) видел настоящий шиндовс только «на картинках», ну или где-нибудь на чужом компьютере.
В разделе «Лучшие дистрибутивы для опытных специалистов» нет самого главного — NixOS. Знакомство с Nix в 2019, а потом установка NixOS и последующая репликация системы на другие свои компьютеры (я мечтал о чём-то таком не один год в виде идеи) в 2020 — это было в некотором роде революционным шокирующим событием для меня за около 10 лет использования дистрибутивов GNU/Linux. Я до последнего не верил что это сработает, учитывая в частности особенности своего рабочего окружения. NixOS — это настоящая жемчужина, аналогов которой нет.
А ошибка может быть только any, по понятным причинам (мы не управляем источником ошибок).
Да нет же, она может иметь строковое представление, как в примере, может быть инстансом Error, может иметь и какую-то более сложную модель данных, и вообще состоять из sum-type-а. Если ошибка имеет тип any, то это плохо сделанная работа. А если считаете, что это ок, то мой совет: выкидывайте TypeScript и не тратьте на него время. Когда он используется таким образом от него больше вреда, как от time-spender-а, чем пользы.
Если же вас смущает, что она не string
Исходя из его был сделан такой вывод? Смущает. Точнее смущает если она не имеет конкретного описанного типа.
В первой части рассматривался пример без типизации. Если нужна типизация, хорошая, а не абы какая, часто приходится где-то жертвовать краткостью, не обязательно в данном конкретном случае. На мой взгляд строгая корректная модель типов гораздо важнее предельной краткости. По-этому я бы вообще не стал сравнивать самый короткий код и самый хорошо типизированный. Т.к. всякие хитрые helper-ы пересобирающие объекты из одного вида в другой порой просто невозможно корректно описать в рамках TypeScript.
Сюда бы immer, а то всё ещё очень страшно выглядит. Особенно если потребуется вложенные объекты менять.
Я бы immutable.js использовал, но для простоты примерра намеренно опустил любые доп. библиотеки.
А затем шаг номер 2: перестать хранить в Redux всякую ерунду вроде статуса загрузки, и перенести это в хуки
Если я к примеру не хочу повторно загружать данные при отмонтировании компонента, то это не подходит. Если я хочу иметь доступ к данным из разных компонентов, то тоже не подходит. И вообще, мне не нравится идея нагружать компоненты логикой загрузки данных откуда-то, предпочитаю делать это в стороне от компонентов (View). От компонентов должен только поступить сигнал «хочу данные».
Это не дело вкуса, и тут не «всё в одном», а всего-лишь самый необходимый минимум для того, чтобы на уровне типов отражалось доступность значения. Т.е. это по сути технически возможный минимум данных (опять же, в контексте корректно-типизированных свойств). На уровне типов у вас прямая связь с состоянием загрузки значения, либо ошибкой, произошедшей во время загрузки.
В качестве варианта можно рассмотреть сборку всех зависимых данных в одно свойство, но тогда это уже будет не MobX, и все оптимизации observable-ов будут рушиться, будет одно большое значение, которое будет постоянно меняться.
В качестве значений — функции с переопределённым методом toString, таким образом newsActions.toString() вернёт NEWS@LOAD_REQUEST, а newsActions(123) вернёт { type: 'NEWS@LOAD_REQUEST', payload: 123 }
Т.е. желать меньшего, чем одна строчка на action невозможно (технически все эти 3 action-а можно и в одну строку записать и уложиться в 80 символов на строку, ещё останется). И это не только action, а сразу и action-creator.
Ваш пример:
export function increment() {
return {
type: 'INCREMENT'
}
}
export function decrement() {
return {
type: 'DECREMENT'
}
}
А что до редьюсеров, в лучших случаях тут тоже всё до одной строчки сводится (ключи объекта внутри квадратных скобок автоматически преобразовываются к строке через вызов метода toString):
Что уже совсем не похоже на подчёркиваемую многословность, напротив.
Но главная проблема MobX, на мой взгляд, это плохая интегрируемость с системой типов. Вы написали что у вас TypeScript среди используемых инструментов. И судя по всему ваша команда, как и подавляющее большинство пользователей TypeScript просто тратите своё время, т.к. если вы не используете мощь типизации (которую TypeScript может дать), вы просто тратите время на резрешение ошибок типов, в то время как Ваш код остаётся плохо типизированным и у вас открытое поле для багов в рантайме.
Взять к примеру то же состояние загрузки, есть у вас допустим какая-нибудь страница, где есть id условной горячей новости дня, из этого id вы генерируете запрос на сервер. Пока вы этот id ниоткуда ещё не загрузили, вам нужно туда что-то записать, ну большинство просто запишет 0, а это в корне неверно. Если вы, полагаясь на собственную внимательность, забудете вручную проверить, загружены ли данные и/или что этот id не равен 0, то получите рантайм баг, т.к. на сервере не будет такого элемента (и это не воображаемый сценарий, а случай из реальной жизни, из личной практики). А в это время TypeScript будет говорить вам что вы всё сделали правильно.
Это можно легко решить на TypeScript с помощью generic-ов и sum-type. Вот мой личный экспериментальный пример с Redux:
Суть в том, что у вас будет несколько базовых generic-ов:
export type Idle<T> =
{ readonly [K in keyof T]: T[K] } & { readonly status: 'idle' };
export type Request<T> =
{ readonly [K in keyof T]: T[K] } & { readonly status: 'loading' };
export type Success<T> =
{ readonly [K in keyof T]: T[K] } & { readonly status: 'success' };
export type Failure<T> =
{ readonly [K in keyof T]: T[K] } & { readonly status: 'failure' };
export type CommonFailure =
Failure<{ errMessage: string }>;
// Самый главный, подходящий для большинства случаев, когда специфичный payload
// есть только для case-а успешной загрузки данных с сервера.
// А для ошибок только текстовое представление ошибки.
export type DataRequest<T> =
Idle<{}> | Request<{}> | Success<T> | CommonFailure;
С помощью пары helper-ов можно проверять исчерпываемость switch-case проверок, а также ограничивать scope проверки action-ов в reducer-ах, т.е. action-ы должны быть обработаны эксплицитно все, но только те, которые имеют отношение к конкретной ветке store-а (можно почитать комментарии к коду по ссылке выше, они оттуда, а также посмотреть примеры использования для большей ясности):
Выброс исключения в ImpossibleCase как правило на самом деле никогда не происходит (если не было использовано магии с any), т.к. type-checker проверяет, что до этой точки выполнения невозможно дойти.
Таким образом можно описать reducer как (можно также смело полагаться на строки в качестве action-type, т.к. type-checker сразу выпадет с ошибкой на опечатку):
ProveExhaustiveness тут проверяет только на уровне типов, в runtime любой action проходит через все reducer-ы. Но в данном случае ограничивается подмножество action-ов, которые могут быть обработаны в данной конкретной ветке стора (намеренно, это необязательно, просто показана такая возможность, и всё во время type-checking-а).
А уже в компоненте потом можно просто проверить в начале:
if (news.status !== 'success') {
if (news.status === 'failure') return <div>We are fucked up: {news.errMessage}</div>;
else return <div>Loading</div>;
}
И всё, дальше уже type-checker-ом будет доказано, что news — это Request<{news: ReadonlyArray<NewsItem>}>. Любые опечатки или обращения к поляем другого типа из общего sum-type-а в то время, как доказано что это другой тип (либо не доказано, что тот, к полю которого обращаемся, либо не исключены проверками типы, у которых такого поля с соответствующим типом этого поля нет), — приведут к ошибке type-checking-а. То, что доктор прописал, машины делают за людей их работу и ловят баги!
Под всё это ещё можно и генерализованный компонент сделать, который будет в одну строчку обрабатывать ошибку или состояние загрузки компонента. Т.е. как-то вроде этого:
if (smth.status !== 'success') return <Failover thatSumType={smth}/>;
А теперь попробуйте переложить эту модель на MobX и тут же поймёте почему это на MobX ложится плохо. Потому что при описании свойств объекта MobX мы имеем фиксированную модель данных, и обернуть её целиком в DataRequest не получится, а получится лишь оборачивать каждое зависимое от состояния загрузки с сервера данных поле в такой generic. В итоге проверять в компоненте на состояние загруженности данных уже придётся каждое поле, к которому обращаемся, хотя они по смыслу сгруппированы. И то вышеописанное для примера поле id горячей новости дня, и список новостей и всё остальное, что зависит от наличия данных, полученных от сервера.
P.S. Местами где-то сумбурно и поверхностно написал, просто тут материала можно на целую статью выделить, если расписывать в деталях.
Мимо чего? Мимо взрослого мира? Да, мимо. Сплошной детский сад, куличики и смешные детские обиды.
Ладно, я понял, ваша аргументация скатилась к реверсивному навязыванию "детского сада" (я уже и не прикину сколько раз вы это повторили, но точно больше десяти, вероятно пытаясь нащупать какое-то уязвимое место?), обвинению собеседника в собственных комплексах, и банальному придуриванию. Я не впечатлён и удаляюсь, мне такой диалог не интересен. Подумайте об этом на досуге, когда в очередной раз будете претендовать на "взрослость" в радостную обнимку с теми "кому плевать".
Да откуда вы взяли что не можем? Можем. Нет у "нас" такой проблемы. Всё нормально увязывается между собой. Используемые из той или иной "стандартной библиотеки" не диктуют функционал внешнего API, как это связано у вас, я вообще не понял. Сигнатура типа вашей библиотеки от этого не меняется. Если где-то что-то не склеивается, и приходится как-то самому композировать разные функции из библиотек, то это не по причине использования той или иной "стандартной библиотеки".
Гарантии вам вообще вряд ли какие-то о поддержке столь долгосрочные даст. Одни языки внезапно и скоротечно умирают, другие, вроде COBOL, могут легко, на удивление, пережить создателей, сильно не изменяя облика. Это сильно эфемернее, чем вам, вероятно, кажется. Перед вами кто-то расписался кровью, что C++ или Java скоротечно не умрут? У каких-то компаний перед вами обязательства?
Если же говорить о чём-то предполагаемом, а не о воображаемых "гарантиях". То за все эти десятилетия жизни Haskell, новые экстеншены только добавлялись и поддерживались, и редко когда какие-то удалялись. Вроде что-то было, но единичное, и экстеншены почти или совсем не используемые и сомнительного назначения.
А соберётся она точно, т.к. я использую параллельно как Stackage-снапшот, который привязан как к версии пакета, так и к GHC, в качестве эталона для сборки. А также Nix, который с ещё большей вероятностью даст собрать и запустить мою программу, как если бы она была собрана в последний раз. Гарантия reproducibility.
Так ведь, выражась более скромно, тем, кто "типизацией не смог проникнуться", — и не место в Haskell, вы разве сами с этим не согласны? Они ведь будут только мучаться что "компилятор их изнуряет своими ошибками, скомпилируй мне уже хоть что-нибудь". Какой с этого вообще прок, идти писать на языке с одной из самых мощных практически используемых систем типов при этом считая что строгая типизация тебе совершенно не нужна?
Это ведь получается всё-равно что TypeScript, который может (хоть и далеко не всё, что мне нужно), но на котором не хотят. В итоге ну какой с этого прок, если при симпатии к самому инструменту я получаю антипатию к проектам, на нём написанном? И вообще не вижу зачем эти люди себя насилуют и рожают обыкновенный JavaScript shitcode тратя свои программисто-часы на бесполезную расстановку as any? Ничего хорошего из этого не вышло, мне такой инструмент не нужен, я могу лишь, как вы ранее сказали, "свои наколенные эксперименты палочкой тыкать, пока никто не видит", а в проекты контрибьютить желания никакого нет. Получилась "мёртвая нагрузка", которую используют потому что модно и так принято в этот год.
Спасибо за наводку. Около месяца аптайма компьютера и браузера под кеш нарезервировалось почти все 32ГБ. После большого перерыва стал пересобирать систему, обновление большое, понадобилось оперативной памяти прилично. И вместо того, чтобы откусить от того, что нарезервировалось, система полезла в своп на диск, на 3,5ГиБ примерно. Разумеется всё начало адово фризиться (впрочем не так долго, т.к. SSD, но тем не менее). Отключил к чертям своп на диске и подключил ZRAM swap, чтобы такого больше не повторялось.
Живу в Финляндии, о*уел немного от прочитанного, надо признаться. А ведь я когда-то выбирал между Германией и Финляндией, ну где быстрее нарисуется хороший случай. Повезло же мне! Розовые очки конечно тоже поблекли в нюансах, но никаких серьёзных потрясений за полтора года не было. Окажись я в Вашей шкуре, плевался бы очень долго. Хотя из перечисленного о весьма и весьма специфичной культуре германского сервиса я был наслышан.
Это у Вас молниеносно по памяти не текло. Если там чего-то недохватило, то в фризы могут быть и незначительными. А вот если у вас что-то пошло не так, и нечто начало аллоцировать как не в себя, да ещё параллельно в несколько потоков/процессов (ну вот сидели Вы, погромировали, экспериментировали, допустили страшное недоразумение в творческом потоке), а Вы и не заметили сначала, то если есть своп, пиши «пропало», потому что как оно доест до свопа, всё повиснет, и проще ресетнуть компьютер, чем ждать пока оно мееееееедленно доест своп (а диск медленный) и встретит свою OOM-смерть. А вот если свопа нет совсем, то OOM такой франкенштайн встретит гораздо быстрее.
И вообще, даже если поверить на слово, что именно swap файл вот так прямо нужен. Давайте может я тогда:
/tmplol
sudo dd if=/dev/zero of=/tmplol/swaplol bs=1M count=4096
sudo mkswap /tmplol/swaplol
sudo chmod 600 /tmplol/swaplol
sudo swapon /tmplol/swaplol
ОС будет мне благодарна? Ведь оперативной памяти у меня, ух, аж на 3 Electron-приложения хватит, положу swapfile прямо туда, в оперативную память. Зачем мне дёргать лишний раз не вечный и медленный диск?
Взаимоисключающие параграфы? «Памяти хватает, система не бюджетная (32Г думаю достаточно для аргумента), зачем мне своп, если я точно знаю, что если система дошла то того, что полезла в своп, то проще нажать Reset, т.к. фризы будут такие, что даже открыть терминал по хоткею и кильнуть вредный процесс может стать невыполнимой задачей.»
В каком месте он тут неправильный? Если:
Ну я уже всех не помню, это было лет 8-9 назад. В качестве DAW Reaper, из VST плагинов GuitarRig, TRex, эквалайзеры, компрессоры, ревербераторы, набор плагинов Wave, Native Instruments, какой-то очень известный барабанный сэмплер, забыл название, я всех уже и не вспомню, и имена не помню все. Какие-то, куда всякого DRM и прочей «защиты от пользователя» напихали, могут и не заработать без плясок. Но те, что мне были нужны, все работали.
Ну для кого-то персонально и настал. Я в своё время, когда убедился, что нужный мне музыкальный софт (который я позже заменил open source решениями) отлично работает из под Wine, то избавился от дуал бута, и за все эти года (наверное уж с десятилетие) видел настоящий шиндовс только «на картинках», ну или где-нибудь на чужом компьютере.
В разделе «Лучшие дистрибутивы для опытных специалистов» нет самого главного — NixOS. Знакомство с Nix в 2019, а потом установка NixOS и последующая репликация системы на другие свои компьютеры (я мечтал о чём-то таком не один год в виде идеи) в 2020 — это было в некотором роде революционным шокирующим событием для меня за около 10 лет использования дистрибутивов GNU/Linux. Я до последнего не верил что это сработает, учитывая в частности особенности своего рабочего окружения. NixOS — это настоящая жемчужина, аналогов которой нет.
Да нет же, она может иметь строковое представление, как в примере, может быть инстансом Error, может иметь и какую-то более сложную модель данных, и вообще состоять из sum-type-а. Если ошибка имеет тип
any
, то это плохо сделанная работа. А если считаете, что это ок, то мой совет: выкидывайте TypeScript и не тратьте на него время. Когда он используется таким образом от него больше вреда, как от time-spender-а, чем пользы.Исходя из его был сделан такой вывод? Смущает. Точнее смущает если она не имеет конкретного описанного типа.
В первой части рассматривался пример без типизации. Если нужна типизация, хорошая, а не абы какая, часто приходится где-то жертвовать краткостью, не обязательно в данном конкретном случае. На мой взгляд строгая корректная модель типов гораздо важнее предельной краткости. По-этому я бы вообще не стал сравнивать самый короткий код и самый хорошо типизированный. Т.к. всякие хитрые helper-ы пересобирающие объекты из одного вида в другой порой просто невозможно корректно описать в рамках TypeScript.
Я бы immutable.js использовал, но для простоты примерра намеренно опустил любые доп. библиотеки.
Если я к примеру не хочу повторно загружать данные при отмонтировании компонента, то это не подходит. Если я хочу иметь доступ к данным из разных компонентов, то тоже не подходит. И вообще, мне не нравится идея нагружать компоненты логикой загрузки данных откуда-то, предпочитаю делать это в стороне от компонентов (View). От компонентов должен только поступить сигнал «хочу данные».
Поле с ошибкой хранится в стороне, никак не связано с самим значением на уровне типов. Не могу сказать, что это самый прекрасный способ описать типы.
Это не дело вкуса, и тут не «всё в одном», а всего-лишь самый необходимый минимум для того, чтобы на уровне типов отражалось доступность значения. Т.е. это по сути технически возможный минимум данных (опять же, в контексте корректно-типизированных свойств). На уровне типов у вас прямая связь с состоянием загрузки значения, либо ошибкой, произошедшей во время загрузки.
Про «не актуально» в начале это относилось к:
Случайно стёр цитату.
Пример на MobX как это могло бы выглядеть:
В качестве варианта можно рассмотреть сборку всех зависимых данных в одно свойство, но тогда это уже будет не MobX, и все оптимизации observable-ов будут рушиться, будет одно большое значение, которое будет постоянно меняться.
Не актуально, когда в последний раз писал на Redux кода у меня было меньше, чем в Ваших MobX примерах.
Пишется простой helper на 10 строк максимум на базе библиотеки
redux-actions
:А далее допустим есть flow загрузки новостей (в качестве примера), создаём:
На каждый action по одной строчке:
В качестве ключей
newsActions
получаем:loadRequest
loadSuccess
loadFailure
В качестве значений — функции с переопределённым методом
toString
, таким образомnewsActions.toString()
вернётNEWS@LOAD_REQUEST
, аnewsActions(123)
вернёт{ type: 'NEWS@LOAD_REQUEST', payload: 123 }
Т.е. желать меньшего, чем одна строчка на action невозможно (технически все эти 3 action-а можно и в одну строку записать и уложиться в 80 символов на строку, ещё останется). И это не только action, а сразу и action-creator.
Ваш пример:
Превратится в:
А что до редьюсеров, в лучших случаях тут тоже всё до одной строчки сводится (ключи объекта внутри квадратных скобок автоматически преобразовываются к строке через вызов метода
toString
):Также нужно заметить, что Вы манипулируете синтаксическими ухищрениями используя ES6, но игнорируя его особенности, вот Ваш пример:
Вместе с action-creator-ами выше это могло быть записано так:
Что уже совсем не похоже на подчёркиваемую многословность, напротив.
Но главная проблема MobX, на мой взгляд, это плохая интегрируемость с системой типов. Вы написали что у вас TypeScript среди используемых инструментов. И судя по всему ваша команда, как и подавляющее большинство пользователей TypeScript просто тратите своё время, т.к. если вы не используете мощь типизации (которую TypeScript может дать), вы просто тратите время на резрешение ошибок типов, в то время как Ваш код остаётся плохо типизированным и у вас открытое поле для багов в рантайме.
Взять к примеру то же состояние загрузки, есть у вас допустим какая-нибудь страница, где есть id условной горячей новости дня, из этого id вы генерируете запрос на сервер. Пока вы этот id ниоткуда ещё не загрузили, вам нужно туда что-то записать, ну большинство просто запишет
0
, а это в корне неверно. Если вы, полагаясь на собственную внимательность, забудете вручную проверить, загружены ли данные и/или что этот id не равен0
, то получите рантайм баг, т.к. на сервере не будет такого элемента (и это не воображаемый сценарий, а случай из реальной жизни, из личной практики). А в это время TypeScript будет говорить вам что вы всё сделали правильно.Это можно легко решить на TypeScript с помощью generic-ов и sum-type. Вот мой личный экспериментальный пример с Redux:
https://github.com/unclechu/typescript-redux-and-data-request-flow-proper-typing-experiment
Суть в том, что у вас будет несколько базовых generic-ов:
С помощью пары helper-ов можно проверять исчерпываемость switch-case проверок, а также ограничивать scope проверки action-ов в reducer-ах, т.е. action-ы должны быть обработаны эксплицитно все, но только те, которые имеют отношение к конкретной ветке store-а (можно почитать комментарии к коду по ссылке выше, они оттуда, а также посмотреть примеры использования для большей ясности):
Выброс исключения в
ImpossibleCase
как правило на самом деле никогда не происходит (если не было использовано магии сany
), т.к. type-checker проверяет, что до этой точки выполнения невозможно дойти.Таким образом можно описать reducer как (можно также смело полагаться на строки в качестве action-type, т.к. type-checker сразу выпадет с ошибкой на опечатку):
ProveExhaustiveness
тут проверяет только на уровне типов, в runtime любой action проходит через все reducer-ы. Но в данном случае ограничивается подмножество action-ов, которые могут быть обработаны в данной конкретной ветке стора (намеренно, это необязательно, просто показана такая возможность, и всё во время type-checking-а).А уже в компоненте потом можно просто проверить в начале:
И всё, дальше уже type-checker-ом будет доказано, что
news
— этоRequest<{news: ReadonlyArray<NewsItem>}>
. Любые опечатки или обращения к поляем другого типа из общего sum-type-а в то время, как доказано что это другой тип (либо не доказано, что тот, к полю которого обращаемся, либо не исключены проверками типы, у которых такого поля с соответствующим типом этого поля нет), — приведут к ошибке type-checking-а. То, что доктор прописал, машины делают за людей их работу и ловят баги!Под всё это ещё можно и генерализованный компонент сделать, который будет в одну строчку обрабатывать ошибку или состояние загрузки компонента. Т.е. как-то вроде этого:
А теперь попробуйте переложить эту модель на MobX и тут же поймёте почему это на MobX ложится плохо. Потому что при описании свойств объекта MobX мы имеем фиксированную модель данных, и обернуть её целиком в
DataRequest
не получится, а получится лишь оборачивать каждое зависимое от состояния загрузки с сервера данных поле в такой generic. В итоге проверять в компоненте на состояние загруженности данных уже придётся каждое поле, к которому обращаемся, хотя они по смыслу сгруппированы. И то вышеописанное для примера поле id горячей новости дня, и список новостей и всё остальное, что зависит от наличия данных, полученных от сервера.P.S. Местами где-то сумбурно и поверхностно написал, просто тут материала можно на целую статью выделить, если расписывать в деталях.
42
Ладно, я понял, ваша аргументация скатилась к реверсивному навязыванию "детского сада" (я уже и не прикину сколько раз вы это повторили, но точно больше десяти, вероятно пытаясь нащупать какое-то уязвимое место?), обвинению собеседника в собственных комплексах, и банальному придуриванию. Я не впечатлён и удаляюсь, мне такой диалог не интересен. Подумайте об этом на досуге, когда в очередной раз будете претендовать на "взрослость" в радостную обнимку с теми "кому плевать".
Да откуда вы взяли что не можем? Можем. Нет у "нас" такой проблемы. Всё нормально увязывается между собой. Используемые из той или иной "стандартной библиотеки" не диктуют функционал внешнего API, как это связано у вас, я вообще не понял. Сигнатура типа вашей библиотеки от этого не меняется. Если где-то что-то не склеивается, и приходится как-то самому композировать разные функции из библиотек, то это не по причине использования той или иной "стандартной библиотеки".
Гарантии вам вообще вряд ли какие-то о поддержке столь долгосрочные даст. Одни языки внезапно и скоротечно умирают, другие, вроде COBOL, могут легко, на удивление, пережить создателей, сильно не изменяя облика. Это сильно эфемернее, чем вам, вероятно, кажется. Перед вами кто-то расписался кровью, что C++ или Java скоротечно не умрут? У каких-то компаний перед вами обязательства?
Если же говорить о чём-то предполагаемом, а не о воображаемых "гарантиях". То за все эти десятилетия жизни Haskell, новые экстеншены только добавлялись и поддерживались, и редко когда какие-то удалялись. Вроде что-то было, но единичное, и экстеншены почти или совсем не используемые и сомнительного назначения.
А соберётся она точно, т.к. я использую параллельно как Stackage-снапшот, который привязан как к версии пакета, так и к GHC, в качестве эталона для сборки. А также Nix, который с ещё большей вероятностью даст собрать и запустить мою программу, как если бы она была собрана в последний раз. Гарантия reproducibility.
Так ведь, выражась более скромно, тем, кто "типизацией не смог проникнуться", — и не место в Haskell, вы разве сами с этим не согласны? Они ведь будут только мучаться что "компилятор их изнуряет своими ошибками, скомпилируй мне уже хоть что-нибудь". Какой с этого вообще прок, идти писать на языке с одной из самых мощных практически используемых систем типов при этом считая что строгая типизация тебе совершенно не нужна?
Это ведь получается всё-равно что TypeScript, который может (хоть и далеко не всё, что мне нужно), но на котором не хотят. В итоге ну какой с этого прок, если при симпатии к самому инструменту я получаю антипатию к проектам, на нём написанном? И вообще не вижу зачем эти люди себя насилуют и рожают обыкновенный JavaScript shitcode тратя свои программисто-часы на бесполезную расстановку
as any
? Ничего хорошего из этого не вышло, мне такой инструмент не нужен, я могу лишь, как вы ранее сказали, "свои наколенные эксперименты палочкой тыкать, пока никто не видит", а в проекты контрибьютить желания никакого нет. Получилась "мёртвая нагрузка", которую используют потому что модно и так принято в этот год.