Комментарии 79
Это фреймворк для создания кастомных серверов.
А зачем?
Потом я решил, что с учетом необходимости при WoW сервере иметь еще и веб-сайт, было бы неплохо, чтобы фреймворк позволял создавать вообще любые серверы. А с учетом архитектуры я могу создавать интересные вещи, например, веб-интерфейс для взаимодействия с вов. Или можно смотреть онлайн перемещение персонажей, мобов и так далее.
Идей вообще у меня еще много и если я увижу интерес к данной статье, я обязательно напишу еще. В частности, я планирую переписать свой WoW сервер, приведенный по ссылке в данном комментарии, на базе моего фреймворка.
Я ждал этого вопроса
Ну так с ответа на него и надо начинать. Потому что непонятно, какую же задачу вы решаете.
Но потом мне пришла идея сделать инструмент для создания серверов наподобие такого.
Нет, понятнее не стало.
Зачем это надо? ну а зачем вообще люди код пишут :) Для саморазвития, наверное. Привлечения сообщества, поиска узких мест, оптимизации, масштабирования. И, возможно, в будущем — выпуска собственной MMO RPG игры.
Т.е. допустим, у меня есть набор уже ранее разработанных миддлвэров и при замене датасэта (т.е. данных, которые я используя для наполнения сервера — мобы, предметы и т.д.) и, возможно, замене некоторых миддлвэров мы можем получить сервер другой игры.
… и вам для этого понадобилась самописная конфигурация на YAML? Печально.
ну а зачем вообще люди код пишут
В том-то и дело, что люди пишут код с разными целями. И от целей зависит подход.
… и вам для этого понадобилась самописная конфигурация на YAML? Печально.А вы можете развить свою мысль? Что конкретно печального вы нашли при использовании YAML в данном случае?
Это анти-паттерн? Или есть проблемы с производительностью?
Просто я не вижу ничего плохого в том, чтобы имея разные конфиги — получать разный набор серверов, не изменяя при этом код.
Что конкретно печального вы нашли при использовании YAML в данном случае?
То, что вы фактически программируете на языке, который для программирования не очень предназначен.
Просто я не вижу ничего плохого в том, чтобы имея разные конфиги — получать разный набор серверов, не изменяя при этом код.
"Плохое" в том, что вы, на самом деле, изменяете код. Просто этот код теперь не на питоне, а на ямле.
То, что вы фактически программируете на языке, который для программирования не очень предназначен.Если быть точнее, я просто указываю порядок выполнения функций. А парсер преобразовывает конфиг в словарь — нативную структуру в пайтоне, которая в качестве значения (да и ключа, в общем-то) может содержать любой тип данных пайтона. И уже после всех преобразований код выполняется в пайтоне. Не в yaml'е.
«Плохое» в том, что вы, на самом деле, изменяете код. Просто этот код теперь не на питоне, а на ямле.Вообще, если посмотреть в сторону других фреймворков, которые используют yaml-конфиги, мы обнаружим немало интересного. Тот же Symfony2 (к примеру), использует yaml со всякими структурами наподобие imports. Или конфиг AWS (template.yml) со всякими доп. командами. Насколько я вижу, это — повсеместная практика и лично я по прежнему не вижу в этом ничего плохого. Не убедили.
Если быть точнее, я просто указываю порядок выполнения функций.
Удивительно, но код на питоне тоже "просто указывает порядок выполнения функций".
И уже после всех преобразований код выполняется в пайтоне. Не в yaml'е.
Но пишете-то вы на yaml.
Вообще, если посмотреть в сторону других фреймворков, которые используют yaml-конфиги, мы обнаружим немало интересного.
Вопрос в том, для чего они их используют.
Или конфиг AWS (template.yml) со всякими доп. командами.
Что за "конфиг AWS"? Вы не про CloudFormation, случайно?
Насколько я вижу, это — повсеместная практика и лично я по прежнему не вижу в этом ничего плохого.
Что — "повсеместная практика"? Задавать поведение системы ее описанием в конфигурации?
Удивительно, но код на питоне тоже «просто указывает порядок выполнения функций».
Но пишете-то вы на yaml.Да, но при этом я не создаю переменных, новых функций и так далее. Тот факт, что я использую конфиг для указывания порядка еще не делает из yaml среды программирования. А то мы прийдем так к выводу, что HTML — все-таки язык программирования (ведь по сути, мы тоже меняем порядок запрограммированных в другом месте тэгов).
Что за «конфиг AWS»? Вы не про CloudFormation, случайно?Да, он. Мы в темплейте объявляем AWS ресурсы.
Вопрос в том, для чего они их используют.
Что — «повсеместная практика»? Задавать поведение системы ее описанием в конфигурации?
В качестве примера возьму тот же AWS конфиг (aws cloudformation template.yml). Разве мы не меняем поведение системы, указывая policies, разные набор ресурсов и т.д.?
Да, но при этом я не создаю переменных, новых функций и так далее.
Новые функции уже создаете — ваш "пайп" уже и есть такая функция. И переменная у вас уже есть — то, по чему вы делаете маршрутизацию.
Тот факт, что я использую конфиг для указывания порядка еще не делает из yaml среды программирования.
Вы используете "конфиг", чтобы задать новый сервер и определить его поведение, собрав это поведение из набора разных элементов. Для меня это программирование.
Да, он. Мы в темплейте объявляем AWS ресурсы.
Так вот. Во-первых, я за последние пару лет написал достаточно этих шаблонов, чтобы понять, что писать их, на самом деле, адски неудобно. А во-вторых, я был не единственным, кто это понял, и поэтому даже сам AWS потихоньку внедряет CDK, который, внезапно, сделан именно на языках программирования.
Разве мы не меняем поведение системы, указывая policies, разные набор ресурсов и т.д.?
Мы в первую очередь задаем конфигурацию системы. Чем больше ее поведения переезжает в этот конфиг, тем сложнее им становится управлять.
Новые функции уже создаете — ваш «пайп» уже и есть такая функция. И переменная у вас уже есть — то, по чему вы делаете маршрутизацию.
Вы используете «конфиг», чтобы задать новый сервер и определить его поведение, собрав это поведение из набора разных элементов. Для меня это программирование.Мы создаем новые ноды DOM дерева, добавляя HTML тэги. HTML пора объявлять языком программирования?
Так вот. Во-первых, я за последние пару лет написал достаточно этих шаблонов, чтобы понять, что писать их, на самом деле, адски неудобно. А во-вторых, я был не единственным, кто это понял, и поэтому даже сам AWS потихоньку внедряет CDK, который, внезапно, сделан именно на языках программирования.
Про CDK — это очень интересное замечание! Мне нужно изучить, чтобы я мог в полной мере проанализировать обстановку.
Мы в первую очередь задаем конфигурацию системы. Чем больше ее поведения переезжает в этот конфиг, тем сложнее им становится управлять.Возможно. Я пока еще не натолкнулся на такие проблемы, поэтому не могу как-то парировать ваше замечание.
Мы создаем новые ноды DOM дерева, добавляя HTML тэги. HTML пора объявлять языком программирования?
Нет, не пора.
Про CDK — это очень интересное замечание! Мне нужно изучить, чтобы я мог в полной мере проанализировать обстановку.
В ту же корзиночку: Pulumi.
Я пока еще не натолкнулся на такие проблемы, поэтому не могу как-то парировать ваше замечание.
Вы, скажем, не натолкнулись на проблему, что для CloudFormation-шаблонов нужен отдельный линтер? Что подсветка синтаксиса в них работает не очень, навигация еще хуже, а рефакторинг — вообще никак?
И то же самое будет с вашими конфигами: вы решили переименовать милдварь, и вам нужно придумывать какой-то тулинг, чтобы поймать все места, где эта мидлварь использовалась, до запуска приложения. Вы решили выделить часто используемый набор мидлварей в какой-то компонент — вы не можете сделать это автоматически.
(я уж молчу про типы параметров и возвращаемых значений — эта проблема, по крайней мере, свойственна всем конвеерным архитектурам, а не только конфигурируемым)
Кстати, любопытно посмотреть на пример хотя бы двух реальных middleware, используемых в вашей архитектуре.
Нет, не пора.Но вы согласны, что хоть пишу-то я на HTML, ноды я генерирую в самом что ни на есть javascript DOM дереве? И добавляя тэг, я использую фактически и функции, и переменные. Это не программирование?
В ту же корзиночку: Pulumi.Спасибо, я гляну. Пока что начал смотреть aws-cdk и навскидку — хотя и переехали в пайтон, там по прежнему много строк. Ну т.е. по сути кусок конфига переехал в пайтон код. В чем суть удобства в таком случае? Со строками, на мой взгляд, проблемы где-угодно одинаковые: пропущенный символ может стать незабываемым дебаг-приключением на пол-дня.
Вы, скажем, не натолкнулись на проблему, что для CloudFormation-шаблонов нужен отдельный линтер? Что подсветка синтаксиса в них работает не очень, навигация еще хуже, а рефакторинг — вообще никак?Честно вам скажу, я не фанат AWS. Я с ним работал всего месяцев 8, на высоконагруженном проекте, но не в качестве архитектора, а в качестве пайтон дэва. И общался с AWS на базовом уровне — лямбды, легкие правки в template.yml, деплой, дебаг, бакеты, динамодб и прочие мелочи. Ну т.е. я понимаю, что для деплоя — это крутая штука, но вот работать с ним — это что-то невероятное. Очень неудобная в плане кодинга/настройки вещь.
И то же самое будет с вашими конфигами: вы решили переименовать милдварь, и вам нужно придумывать какой-то тулинг, чтобы поймать все места, где эта мидлварь использовалась, до запуска приложения. Вы решили выделить часто используемый набор мидлварей в какой-то компонент — вы не можете сделать это автоматически.Да, тут я с вами согласен. Это — хорошее замечание. Более того, я с этим столкнулся еще до релиза и думаю, как решить это минимальными потерями.
Кстати, любопытно посмотреть на пример хотя бы двух реальных middleware, используемых в вашей архитектуре.Например
Но вы согласны, что хоть пишу-то я на HTML, ноды я генерирую в самом что ни на есть javascript DOM дереве?
Нет, не согласен. Вы не генерируете никакие ноды. Их может никогда не быть.
Ну т.е. по сути кусок конфига переехал в пайтон код. В чем суть удобства в таком случае?
В том, что можно ресурс запихнуть в переменную, и из других ресурсов обращаться к переменной. Необъявленные переменные прекрасно ловит линтер питона (и компиляторы для компилируемых языков). В том, что можно повторяющееся создание ресурса запихнуть в компонент, который положить в модуль, распространяемый средствами языка, и переиспользовать между разными проектами.
Честно вам скажу, я не фанат AWS.
… может тогда не стоит его приводить как пример?
Очень неудобная в плане кодинга/настройки вещь.
Вы знаете более удобную в плане кодинга/настройки облачную инфраструктуру?
Например
Это инфраструктура, а не прикладной код. Но даже в этом примере хорошо видно, что у вас все взаимодействие завязано на строковые ключи и словари. Знал я один аналогичным образом устроенный конвеер, OWIN, так вот, прикладные разработчики с ним всегда работали через типизированные обертки, потому что иначе уследить за всеми этими константами невозможно. А у вас даже констант нет, вы kwargs.pop('session_storage')
в каждом методе повторяете зачем-то.
Но, повторюсь, эта проблема не уникальна для вашего решения, вы просто тоже ее повторили.
Нет, не согласен. Вы не генерируете никакие ноды. Их может никогда не быть.А как насчет того, что DOM — это javascript представление HTML дерева? Я ничего не придумываю developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model
В том, что можно ресурс запихнуть в переменную, и из других ресурсов обращаться к переменной. Необъявленные переменные прекрасно ловит линтер питона (и компиляторы для компилируемых языков). В том, что можно повторяющееся создание ресурса запихнуть в компонент, который положить в модуль, распространяемый средствами языка, и переиспользовать между разными проектами.Ну а в template есть !Ref и необъявленные ресурсы отлавливаются хуками aws deploy. В чем тогда разница?
… может тогда не стоит его приводить как пример?Я считаю, что стоит, раз у меня есть опыт работы с ним. Я ведь не умничаю, а правда думаю то, что пишу — и с благодарностью принимаю наставления от более опытных людей :)
Это инфраструктура, а не прикладной код.Прикладной код появится чуть позже. Как я писал ранее, я планирую переписать свой wow сервер на базе моего фреймворка, чтобы определить, какие вообще есть проблемы и подводные камни.
А у вас даже констант нет, вы kwargs.pop('session_storage') в каждом методе повторяете зачем-то.Да, тут я согласен. Есть такая проблема. Я пока не придумал, как грамотно переместить все строки. Чтобы это было еще и удобно.
А как насчет того, что DOM — это javascript представление HTML дерева?
DOM — это действительно javascript-представление HTML дерева. Но это не значит, что HTML-узел обязательно становится узлом DOM. Представьте себе браузер без поддержки JS, и вы поймете, почему.
HTML — это "всего лишь" язык разметки. Дальше он отображается визуализатором или редактором, или парсится конвертером, или еще что-то.
Ну а в template есть !Ref и необъявленные ресурсы отлавливаются хуками aws deploy. В чем тогда разница?
Ровно в том, что хуки aws deploy немножко сложнее встроить в CI-процесс (и локальную разработку), нежели линтер для питона или компилятор для C#. И в том, что IDE по !Ref
ходит немножко хуже, чем по объявлениям и использованиям переменных.
Прикладной код появится чуть позже.
Я вам настоятельно не рекомендую публиковать фреймворк до появления какого-то проекта, хотя бы игрушечного, сделанного с его помощью. Потому что пока вы не соберете проблем, возникающих в прикладном применении, вы не понимаете слабых (и сильных) мест вашего фреймворка.
Я пока не придумал, как грамотно переместить все строки.
Еще одно свидетельство того, что ваш фреймворк к релизу не готов.
Удивительно, конечно, что эта проблема не остановила вас на, не знаю, второй написанной мидлвари. Меня бы точно остановила, и пока я бы ее не решил, я бы дальше не двигался.
(Точнее, никакого "бы", я по этой дорожке ходил сильно больше одного раза. Только, правда, не в питоне, поэтому не уверен, насколько мои решения для вас применимы.)
Представьте себе браузер без поддержки JS, и вы поймете, почему.В этом плане — согласен.
HTML — это «всего лишь» язык разметки. Дальше он отображается визуализатором или редактором, или парсится конвертером, или еще что-то.В моем понимании, HTML — просто текст, если он не парсится. Будь то библиотека (например) или браузер.Но когда HTML парсится, мы же в любом случае создаем функции и переменные. Например, встраивание видео через HTML или iframe для рендеринга задействует API браузера, который, в свою очередь, наверняка задействует API операционной системы. Нам же нужны драйвера для воспроизведения видео/аудио.
немножкоможете объяснить, что в данном контексте означает «немножко»? И какая IDE приведена в качестве примера? PyCharm?
Я вам настоятельно не рекомендую публиковать фреймворк до появления какого-то проекта, хотя бы игрушечного, сделанного с его помощью. Потому что пока вы не соберете проблем, возникающих в прикладном применении, вы не понимаете слабых (и сильных) мест вашего фреймворка.хорошее замечание. Вообще, 0.0.1 была опубликована с целью сбора как раз таки проблем. Т.е. я никого не заставляю силком устанавливать фреймворк — а на pypi (как и на github) вроде пока нет ограничений по заливке. Моя основная цель как раз — привлечь сообщество. Вот с вами, например, у нас получилась интересная и полезная (по крайней мере, для меня) дискуссия. Я бы вряд ли обратил внимание на многие проблемы, если бы продолжал разрабатывать тихо сам с собой, как я делал примерно год до публикации фреймворка. До этого я его переписал с нуля несколько раз и было произведено множество экспериментов. А wowcore я начал писать еще осенью 2018. На хабре есть несколько статей, где я рассматривал проблемы оптимизации python кода (как раз оттуда)
Но когда HTML парсится, мы же в любом случае создаем функции и переменные.
Нет, не создаем.
можете объяснить, что в данном контексте означает «немножко»?
В данном примере "немножко" означает, что за (приблизительно) два года работы с CloudFormation я так и не смог ни настроить для него CI-верификацию, ни поддержку в редакторе за пределами подсветки YAML (заметим, именно базового YAML, а не специфических команд CFN). Для питона у меня есть и то, и другое.
И какая IDE приведена в качестве примера? PyCharm?
VS Code.
Точнее, так: в качестве того, как надо, я рассматриваю Visual Studio + ReSharper или Rider (как несложно догадаться, не для питона, а для C#). Для питона, с которым я работаю в VS Code, я смог получить, не знаю, где-то треть от этой функциональности (или 1/5), но мне больше и не надо, потому что я с ним и работаю в десятки раз меньше. А для CloudFormation YAML — ноль, не считая подсветки, хотя с ним я работаю намного больше, чем с питоном.
Нет, не создаем.Т.е. HTML рендерится с помощью магии? Не благодаря тому, что браузер создает новые объекты? (Я надеюсь, вы не будете меня заставлять делать реверс инжиниринг браузера).
В данном примере «немножко» означает, что за (приблизительно) два года работы с CloudFormation я так и не смог ни настроить для него CI-верификацию, ни поддержку в редакторе за пределами подсветки YAML (заметим, именно базового YAML, а не специфических команд CFN). Для питона у меня есть и то, и другое.
VS Code.Честно, не знаю насчет VS Code, лично мне он сразу не понравился — не считаю его удобным. Но для Intellij есть плагины, вот например.
Т.е. HTML рендерится с помощью магии?
Нет, HTML рендерится с помощью какого-то программного кода. Но когда мы пишем HTML, мы никак не контролируем код, который его рендерит, и этот код не является нашей задачей. Нашей задачей является отображение.
Честно, не знаю насчет VS Code, лично мне он сразу не понравился — не считаю его удобным. Но для Intellij есть плагины, вот например.
Для VS Code тоже есть плагины. Но они, как говорилось выше, не справляются на том уровне, на котором мне нужно.
Просто я не вижу ничего плохого в том, чтобы имея разные конфиги — получать разный набор серверов, не изменяя при этом код.
Я, пожалуй, с другой стороны зайду: а что хорошего в том, что для получения "разного набора серверов" надо править не py
-файл, а yaml
-файл?
Но все зависит, конечно, от набора задач. Они могут быть очень специфические, поэтому вопрос спорный.
Т.е. у меня один файл, где описан весь функционал.
И что вам мешает сделать то же самое в одном py
-файле?
(я не буду сейчас вдаваться в философский вопрос, нужно ли в одном файле описывать адреса серверов и порядок функций в конвеере, это вам в 12 Factor App)
Собственно, в каком-нибудь ASP.NET Core это давно и прекрасно делается в классе Startup
, он же конвенционально файл Startup.cs
, и прекрасно работает.
И что вам мешает сделать то же самое в одном py-файле?Повторюсь, целостность. Я предпочел все настройки приложения хранить в одном месте — а набор (а также порядок) миддлвэров я отношу к настройкам.
Повторюсь, целостность. Я предпочел все настройки приложения хранить в одном месте
… и почему этим местом не может быть py
-файл?
а набор (а также порядок) миддлвэров я отношу к настройкам.
А вот это то место, где мы с вами радикально расходимся во взглядах. Потому что для меня миддлвари — это и есть приложение.
… и почему этим местом не может быть py-файл?В py-файле у меня код самой функции. В yaml-файле — путь к ней. А тэгом я указываю парсеру, что этот путь нужно преобразовать именно в функцию.
А вот это то место, где мы с вами радикально расходимся во взглядах. Потому что для меня миддлвари — это и есть приложение.И для меня тоже. Я лишь имел ввиду, что в yaml-конфиге я влияю только на то, какие миддлвэры будут использованы и в каком порядке. Пайп, роутер, инфинит луп — это по сути тоже миддлвэры.
В py-файле у меня код самой функции. В yaml-файле — путь к ней.
И почему это не может быть просто два py
-файла?
И почему это не может быть просто два py-файла?Потому, что для меня py-файл ассоциируется с понятием «исполняемый код», а yaml-файл — ассоциируется с понятием «конфиг». Это такое контекстное разграничение.
Ну то есть никакого технического смысла в этом нет, это проистекает только из ваших субъективных ассоциаций с типами файлов?
А вы считаете, что данный подход некорректный?
Да, считаю. Весь тред выше — это объяснение того, почему.
TL/DR:
- разделение на "исполняемый код" и "конфигурацию" у вас субъективно, и отличается от (настолько же субъективного) моего. Это означает, что будут разногласия, что куда положить, которые для проекта общего пользования плохи.
- вы не получаете никаких технических преимуществ от такого подхода (или не смогли их продемонстрировать)
- при этом есть ряд очевидных недостатков с точки зрения разработки; в частности, полное отсутствие инструментальной поддержки
Проще говоря, недостатки есть, а преимуществ, кроме соответствия вашим ассоциациям, нет.
Мне гораздо интереснее узнать, какой смысл вам мучать автора всеми этими вопросами (и тратить на это своё время)?
разделение на «исполняемый код» и «конфигурацию» у вас субъективно, и отличается от (настолько же субъективного) моего. Это означает, что будут разногласия, что куда положить, которые для проекта общего пользования плохи.Я думаю, это дело привычки. В конечном итоге, все те инструменты, которые облегчают работу с языками программирования (forwarding, например) — появились не на пустом месте. И все когда-то были первопроходцами. И если найти (или реализовать) инструменты для работы с yaml — в контексте моего фреймворка, то еще надо будет посмотреть, что с чем в итоге будет тягаться.
вы не получаете никаких технических преимуществ от такого подхода (или не смогли их продемонстрировать)Ну почему, при наличии конфига в py-файле все константы будут храниться в памяти, а при загрузке из yaml-файла мы загружаем необходимые данные и выгружаем файл из памяти. Исправьте меня, если я заблуждаюсь.
при этом есть ряд очевидных недостатков с точки зрения разработки; в частности, полное отсутствие инструментальной поддержки
Проще говоря, недостатки есть, а преимуществ, кроме соответствия вашим ассоциациям, нет.Пока нет. Благодаря вам, я взглянул на фреймворк с другой стороны. И на документацию к нему, и на его проблемы. Я уже в процессе переосмысления некоторых вещей. И хотя я хочу дойти до конца в плане работы с приложением на базе idewavecore через yaml-конфиг, я допускаю, что возможно релиз версии 1.0.0 будет базироваться уже на других принципах.
Подводя итог вышесказанному, я бы хотел добавить ваш никнейм в раздел благодарностей README моего фреймворка. Если вы не будете сопротивляться.
P.S. я за продолжение дискуссии.
Я думаю, это дело привычки.
… и почему вы думаете, что я — или любой другой разработчик, который пришел воспользоваться вашим фреймворком — заинтересован менять свои привычки под ваши?
И все когда-то были первопроходцами.
В контексте первопроходцев очень поучительно сравнивать истории Скотта и Амундсена. Первопроходцами были оба. Но с полюса вернулась только одна команда.
И если найти (или реализовать) инструменты для работы с yaml — в контексте моего фреймворка, то еще надо будет посмотреть, что с чем в итоге будет тягаться.
Все еще не понятно, зачем это делать, когда есть языки, где это уже реализовано. Вы все время будете играть в догоняющего (как, собственно, в него же играла команда поддержки CloudFormation templates).
а при загрузке из yaml-файла мы загружаем необходимые данные и выгружаем файл из памяти.
Если уж вы считаете память, не забудьте посчитать память, потраченную на модуль загрузки yaml-файла. И, в частности, на все константы, которыми вы описываете узлы в этом файле.
Это такие пренебрежимые спички получатся в любую сторону, что их не стоит даже упоминать.
… и почему вы думаете, что я заинтересован менять свои привычки под ваши?Это в принципе может ждать любого человека, который захотел попробовать новую технологию. Можно и не менять. Например, я встречал людей, которые даже перейдя на пайтон продолжали писать аля c++. Это все дело привычки.
В контексте первопроходцев очень поучительно сравнивать истории Скотта и Амундсена.Ваш пример только подтверждает сказанное мной насчет первопроходцев. Еще Аристотель (Метафизика) писал про то, что опыт возможен благодаря памяти (ну т.е. надо обладать каким-то датасэтом, чтобы делать правильные выводы). А из того, что я прочитал об экспедиции Скотта, он, к примеру, в отличие от Амундсена (который запечатал баки с топливом), не знал про «ползучесть» топлива. И там много нюансов было. Но тем не менее, роль первопроходцев велика в первую очередь потому, что на основе их открытий и на основе предыдущего опыта делаются либо новые открытия, либо улучшаются имеющиеся.
Все еще не понятно, зачем это делать, когда есть языки, где это уже реализовано. Вы все время будете играть в догоняющего (как, собственно, в него же играла команда поддержки CloudFormation templates).
Создавая фреймворк, я рассуждал с точки зрения удобства подхода к разработке типов серверов, с которыми лично я имел дело. Опять же, когда у меня будет готов прототип wow-сервера (и он будет удачен) — я напишу об этом пост.
Если уж вы считаете память, не забудьте посчитать память, потраченную на модуль загрузки yaml-файла. И, в частности, на все константы, которыми вы описываете узлы в этом файле.Это надо будет подебажить. Как и константы. Мне теперь стало интересно, остается ли то и другое в памяти постоянно.
Это в принципе может ждать любого человека, который захотел попробовать новую технологию.
Вот и вопрос: а зачем, собственно? Я обычно пробую новые технологии, потому что есть какая-то причина.
А из того, что я прочитал об экспедиции Скотта, он, к примеру, в отличие от Амундсена (который запечатал баки с топливом), не знал про «ползучесть» топлива.
А еще он, к примеру, взял с собой пони вместо собак.
Но тем не менее, роль первопроходцев велика в первую очередь потому, что на основе их открытий и на основе предыдущего опыта делаются либо новые открытия, либо улучшаются имеющиеся.
Угу. Проблема, однако же, в том, что люди иногда считают себя первопроходцами, и на этом основании отвергают существующий опыт.
Создавая фреймворк, я рассуждал с точки зрения удобства подхода к разработке типов серверов, с которыми лично я имел дело.
Это снова подтверждает мой ранний тезис: вы, фактически, удовлетворяете свою субъективную потребность. Это, конечно, приятно, но вот польза этого для остальных неочевидна.
«Прекрасно работает» — это ведь, как ныне принято считать, ещё не все, что нужно от программы. Вообще-то любая хорошо отлаженная программа прекрасно работает, но не любая — хорошо читается, понимается, модифицируется и отлаживается.
И вот как раз ASP.NET Core, на мой взгляд, никак не может служить положительным примером описания функционала программы, который бы удовлетворял этим пожеланиям.
Например — потому что слишком много в нем избыточных элементов: да хоть тех же скобок вокруг параметров методов, которые нужны, потому что описателями конфигурации по факту служат вызовы методов.
А ещё — потому что принятый в ASP.NET Core стиль поощряет писать длинные и сложные, не охватываемые одним взглядом «фразы» — цепочки вызовов методов-описателей конфигурации (через точку). Причем вызовы в этих цепочках могут включать в себя (и часто включают) не только короткие константы и выражения, но и громоздкие лямбда-функции — многострочные, с фигурными скобками и даже с другими лямбда-функциями в вызовах методов внутри себя. А ещё в цепочке вызова по дороге запросто может смениться тип объекта, с которым идет работа, и часть цепочки будет работать уже с другим классом, а не с тем, с которым работа шла изначально. Например, смотрите вы длинную цепочку вызовов методов IServiceCollection, описывающюю конфигурирацию контейнера сервисов, и по невнимательности упускаете, что в этой цепочке парой строк выше того места, где вы сейчас смотрите, стоял вызов не AddOptions(), а AddOptions<какой-то-тип>() (не правдали, это несложно спутать?).
И внезапно для себя вы уже смотрите на описание конфигурирования не контейнера сервисов, а параметра(Option) это самого «какого-то-типа» — а названия-то методов там есть весьма похожие, сразу в глаза разница может и не броситься (особенно если вы ещё не все эти методы назубок знаете).
Так что потребность в хорошо читаемом средстве записи конфигурации приложения — она IMHO есть, и она отнюдь не удовлетворена. Другое дело, насколько эту потребность удовлетворяет работа автора — об этом я, не будучи ни знатоком YAML, ни знатоком Python, судить не рискую
Ну во-первых, я привел это как пример того, что все "описание" приложения может поместиться в одном файле с кодом, а не того, что это лучший подход в мире.
Во-вторых, "хорошо/плохо читается" — это субъективно. Вам плохо читается, мне хорошо читается. Субъективно это. Я вот, например, с описанными вами проблемами не сталкивался.
При этом когда "описание" сделано на том же языке, что и сама программа, вы получаете вполне объективный бонус в виде работающего инструментария — того же самого, которым вы пользовались при, собственно, разработке программы. И этот инструментарий может, например, указать вам, что вы написали несуществующую команду, показать описание команды при наведении мышкой и полную имплементацию — при Ctrl-Click.
Ну а в-третьих, да, действительно запрос на хорошо читаемую конфигурацию — он есть. И действительно, для небольших несложных примеров YAML обычно выигрывает у языков программирования в читаемости (как бы субъективно это ни было). Проблемы начинаются тогда, когда ваши примеры усложняются. В YAML, будучи тем, кто пишет конфигурацию, вы не можете пойти и сказать: теперь вместо
services:
- OptionsManager:
- IOptions: singleton
- IOptionsSnapshot: scoped
- OptionsMonitor:
- IOptionsMonitor: singleton
- OptionsFactory: IOptionsFactory
я буду писать
services:
- AddOptions
Эту функциональность вам должен предоставить тот, кто разрабатывал чтение конфигурации. А это значит, что ваши возможности по улучшению читаемости резко ограничены.
Ну во-первых, я привел это как пример того, что все «описание» приложения может поместиться в одном файле с кодом, а не того, что это лучший подход в мире.
Что это пример — это сразу было понятно. Но этот пример сразу же проявил и почти неизбежный недостаток такого подхода — описывать конфигурацию приложения, набранного из компонентов, на обычном языке императивного программирования: такой язык для этого приспособлен не наилучшим образом, не для того он был изначально предназначен.
Во-вторых, «хорошо/плохо читается» — это субъективно.Тут спорить не о чем — читает-то конкретный субъект. Обсуждать имеет смысл, насколько хорошо или плохо это для массы — но это явно не стоит делать в комментариях к чужой статье. Так что почему конфигурация в ASP.NET Core плохо читается лично мне — это тоже в качестве примера.
При этом когда «описание» сделано на том же языке, что и сама программа, вы получаете вполне объективный бонус в виде работающего инструментария — того же самого, которым вы пользовались при, собственно, разработке программы.Или — не получаете. Например, если вы смотрите исходный код библиотеки где-нибудь в интернете — бонус от IDE вы не получаете, и надо собственными глазами разбираться: для объекта какого типа вызван метод, если в метод передается делегат — каковы типы параметров этого делегата (лямбда-функция ведь типы своих параметров скрывает просто отлично) и т.д.
В YAML, будучи тем, кто пишет конфигурацию, вы не можете пойти и сказать: теперь вместо… я буду писать…
Что YAML в макросы не умеет — согласен, что это реально плохо для данного применения: сходных наборов компонентов в программе может быть реально много.
В общем, из обсуждения уже проглядывает вывод — проблема с читаемостью конфигурации хоть и есть, но обсуждаемое то-что-должно-стать-фреймворком по факту ее решает «не слишком хорошо».
Думаю, на этом дискуссию пока можно закончить.
описывать конфигурацию приложения, набранного из компонентов, на обычном языке императивного программирования: такой язык для этого приспособлен не наилучшим образом, не для того он был изначально предназначен.
Проблема в том, что никакой язык для этого изначально не был предназначен. Будем еще один внешний DSL плодить?
Или — не получаете. Например, если вы смотрите исходный код библиотеки где-нибудь в интернете — бонус от IDE вы не получаете, и надо собственными глазами разбираться:
В том-то и дело, что получаете.
Точнее, как. В худшем случае, вы получаете ситуацию не хуже, чем с обычным текстовым файлом. Прочитать все равно можно, а имена надо выбирать говорящие. Но в среднем случае (aka GitHub) вы уже получаете синтаксическую подсветку, а иногда и навигацию. В хорошем случае (VS Code Online) вы получаете то же, что и с обычным кодом.
Но пойнт в том, что когда с этим кодом работает разработчик, у него есть IDE. И в этом случае преимущества прямо сияют. А в случае с самописной конфигурацией везде одинаково плохо.
Что YAML в макросы не умеет
В том-то и дело, что это не "макросы". Это обычное структурное программирование, позволяющее уменьшать сложность.
обсуждаемое то-что-должно-стать-фреймворком по факту ее решает «не слишком хорошо».
Обсуждаемое даже не задумывалось о читаемости. Я такой задачи нигде не видел.
Проблема в том, что никакой язык для этого изначально не был предназначен. Будем еще один внешний DSL плодить?
А почему бы и нет? Делают же ведь так для других целей. Вон, MS впихнула свой LINQ в .NET — и я что-то не слышал, чтобы кто-то огорчился (Хотя LINQ, насколько я заметил, используют чаще не как язык со своим ситаксисом, а как набор методов расширения).
А в где-то в 80-е — 90-е IBM примерно то же самое IBM (а вслед за ней и другие) проделывала и с SQL: был тогда такой Embedded SQL.
В худшем случае, вы получаете ситуацию не хуже, чем с обычным текстовым файлом. Прочитать все равно можно, а имена надо выбирать говорящие.
Я как вот как раз про наихудший случай и писал. Конкретно — про использование исходных текстов в качестве справочной информации, замены документации. А в нынешнее время торжества открытого исходного кода (и, как следствие — снижения внимания к качеству документации) этим приходится заниматься все чаще и чаще.
Ну а подсветка синтаксиса — она и в текстовых редакторах нынче есть (даже в древнем Far manager). Только вот прослеживать зависимости, что и откуда, в сложной конфигурации она мало помогает.
А в случае с самописной конфигурацией везде одинаково плохо.
Нет, не одинаково. В отсутсвие подпорок от IDE приходится решать задачу читаемости другими средствами. В частности — синтаксическими. Другой вопрос, как это получается в реальности, но стимул решать есть. Ну и «самописный» — это в наше время развития плагинов тоже не приговор, инструмент для работы с описанием конфигурации сделать-то можно. Только, наверное, это уже — не про обсуждаемый «фреймворк».
В том-то и дело, что это не «макросы». Это обычное структурное программирование, позволяющее уменьшать сложность.
Структурное программирование — это не отсюда, это — форма императивного программирования. А тут мы имеем, если смотреть на семантику, декларативные элементы: в вашем примере мы объявляем, что фраза AddOptions должна пониматься как перечисленный ниже набор фраз. А то, что конкретно в ASP.NET это приходится делать вызовом подпрограммы, которая там тоже что-то вызывает внутри себя — это уже издержки реализации декларативных элементов с помощью императивного языка.
Обсуждаемое даже не задумывалось о читаемости. Я такой задачи нигде не видел.
Ну, насчет «не задумывалось» — я бы так категорично не говорил, ибо телепатическими способностями не владею. Но то, что про это толко в статье не написано — это факт.
И это кстати — основание свернуть дискуссию: она ушла далеко в сторону от обсуждения конкретной статьи.
Так что по этому вопросу имеет смысл написать отдельную статью, и обсудить такие вопросы там: там и аргументацию можно изложить почетче, а, глядишь, и участников дискуссии станет побольше, чем только мы с вами тут.
Вон, MS впихнула свой LINQ в .NET — и я что-то не слышал, чтобы кто-то огорчился
LINQ — это не внешний DSL, а внутренний. Для него изначально была полная языковая поддержка.
Я как вот как раз про наихудший случай и писал. Конкретно — про использование исходных текстов в качестве справочной информации, замены документации
Я это делаю либо на гитхабе, где уже есть навигация, либо локально в IDE, где тоже есть навигация. Поэтому ваш "наихудший случай" со мной не происходит практически никогда.
В отсутсвие подпорок от IDE приходится решать задачу читаемости другими средствами. В частности — синтаксическими. Другой вопрос, как это получается в реальности, но стимул решать есть.
Не, нету. Есть стимул бросить и уйти туда, где есть поддержка.
Ну и «самописный» — это в наше время развития плагинов тоже не приговор, инструмент для работы с описанием конфигурации сделать-то можно.
Можно, но это та самая позиция догоняющего.
А тут мы имеем, если смотреть на семантику, декларативные элементы: в вашем примере мы объявляем, что фраза AddOptions должна пониматься как перечисленный ниже набор фраз.
Неее, это вы объявляете. А я хочу иметь именно нормальные возможности программирования. Валидировать состояние, принимать решения и так далее.
LINQ — это не внешний DSL, а внутренний. Для него изначально была полная языковая поддержка.
Поддержка поддержкой, однако синтаксис и семантика его всесьма чужеродны императивным языкам, вроде C#. Так же, как и встроенного SQL — языкам, куда он встраивался: COBOL, FORTRAN, C…
Впрочем, я если я правильно понял, вас беспокоят прежде всего удобство работы, а не семантический разрыв между частями программы? Тогда да, дла этого интегрированность — это важно, и большая фирма об этом позаботится всяко лучше, нежели всякие сторонние разработчики со своими доморощенными фремворками.
Я это делаю либо на гитхабе, где уже есть навигация, либо локально в IDE, где тоже есть навигация. Поэтому ваш «наихудший случай» со мной не происходит практически никогда.
Ну что тут сказать? Вы — молодец, но почему-то не всем так везет: я даже тут, на Хабре видел жалобы, что людям такое мешает code review делать, потому что на телефоне IDE нет.
Не, нету. Есть стимул бросить и уйти туда, где есть поддержка.
Есть разные пути. И если вы предпочитаете ходить только по хоженным дорожкам, то есть и люди с другими предпочтениями. И нельзя сказать, что они заведомо неправы — иногда на нехоженых дорожках можно найти что-то очень ценное.
Можно, но это та самая позиция догоняющего.Да, но не всегда это заведомо плохо. В «позиции догоняющего», например начинали свой старт такие продукты — будущие лидеры, как IBM PC, видеокарты NVidia, браузеры Internet Explorer и Google Chrome…
Неее, это вы объявляете.
Не, в данном случае это не я объявляю — это разработчики .NET объявляют. То, что вы написали — это изложенное в других терминах содержимое исходного кода метода OptionsServiceCollectionExtensions.AddOptions из библиотеки времени выполнения .NET (только там объявления содержат семантику проверки на дубль и объявление реализации ещё одного интерфейса, кроме перечисленных). И это — именно объявления: они пока записываются «куда-то» и будут обработаны (описанные в них реализации — добавлены в контейнер сервисов) позднее, когда придет время.
А я хочу иметь именно нормальные возможности программирования. Валидировать состояние, принимать решения и так далее.
Желание понятное. Но другой стороной этого желания является повышенная сложность его реализации, по сравнению с тем, чтобы просто сказать, что «будем использовать то-то и то-то» (декларативный подход).
Впрочем, некоторые аспекты такого желания можно удовлетворить и при декларативном подходе: к примеру, XML можно провертить на соответствие его схеме. Другое дело, что в возможности выбранных авторам статьи средств реализации это не входит.
PS Как-то дискуссия сильно уж оторвалась от предмета статьи. Так что продолжу обсуждение затронутых тем как-нибудь попозже, при случае.
Поддержка поддержкой, однако синтаксис и семантика его всесьма чужеродны императивным языкам, вроде C#.
C# — мультипарадигменный язык. Я лично не люблю query syntax, и пользуюсь методами, но это на вкус и цвет.
Впрочем, я если я правильно понял, вас беспокоят прежде всего удобство работы, а не семантический разрыв между частями программы?
А где там семантический разрыв?
Не, в данном случае это не я объявляю — это разработчики .NET объявляют.
Где-то что-то перепуталось. Разработчики .NET (хотя, на самом деле. не .NET, а Extensions, но не важно) сделали обычный метод-расширение. Он "декларативен" приблизительно настолько же, насколько любое другое расширение.
И это — именно объявления: они пока записываются «куда-то» и будут обработаны (описанные в них реализации — добавлены в контейнер сервисов) позднее, когда придет время.
А это, кстати, просто неправда. Они добавляются в контейнер именно сейчас (это хорошо видно по тому, что, скажем, следующий TryAddX
уже ничего не добавит). Другое дело, что контейнер пока не собран, это следующий этап.
Но другой стороной этого желания является повышенная сложность его реализации, по сравнению с тем, чтобы просто сказать, что «будем использовать то-то и то-то» (декларативный подход).
Странно, для меня это выглядит ровно наоборот: чтобы сделать то, что мне нужно, не надо ничего реализовывать, надо просто взять готовый язык программирования. А "просто сказать" требует разработки DSL, к которому нужен синтаксис, парсер, утилиты и так далее.
Впрочем, некоторые аспекты такого желания можно удовлетворить и при декларативном подходе: к примеру, XML можно провертить на соответствие его схеме.
… и это никак не поможет понять, что в нем указана существующая в исполняемом коде функция.
А где там семантический разрыв?
Между декларативным и императивным подходами: семантика у них все-таки разная.
Где-то что-то перепуталось. Разработчики .NET (хотя, на самом деле. не .NET, а Extensions, но не важно) сделали обычный метод-расширение. Он «декларативен» приблизительно настолько же, насколько любое другое расширение.
Однако метод этот по факту содержит набор деклараций вида:
«для реализации интерфейса IXxx использовать вновь созданный экземпляр класса Xxx (или заранее созданный объект, или результат возвращяемый указанным делегатом — есть и такие варианты), и экземпляр этот должен создаваться тогда-то и тогда и существовать столько-то и столько-то».
Что это по сути, если не декларация?
А это, кстати, просто неправда. Они добавляются в контейнер именно сейчас (это хорошо видно по тому, что, скажем, следующий TryAddX уже ничего не добавит). Другое дело, что контейнер пока не собран, это следующий этап.
IServiceCollection — это никоим образом не контейнер сервисов: интерфейс контейнера сервисов IServiceProvider в объекте, которыый его реализует, отсутствует, а потому получить оттуда реализацию сервиса для каких либо надобностей никак нельзя.
Он предтавляет собой всего лишь список деклараций, о том, что должно в будущем оказаться в в контейнере сервисов.
Ну, и немного занудства: в .NET 5 Options — это уже часть рантайма, и даже в исходных текстах соответсвующий набор файлов перенесен из репозитория extensions.
Странно, для меня это выглядит ровно наоборот: чтобы сделать то, что мне нужно, не надо ничего реализовывать, надо просто взять готовый язык программирования. А «просто сказать» требует разработки DSL, к которому нужен синтаксис, парсер, утилиты и так далее.
Предполагается, что DSL и средства работы с ним вам тоже предоставят. Это снимает ваше возражение?
… и это никак не поможет понять, что в нем указана существующая в исполняемом коде функция.
Я же говорю — «некоторые аспекты». Не все — ибо нет в мире совершенства. Кстати, точно так же можно нарваться на несуществующую функцию, и безо всякого дополнительного языка — работая, к примеру, с ASP.NET MVC.
Между декларативным и императивным подходами: семантика у них все-таки разная.
А где вы там нашли декларативный подход?
Что это по сути, если не декларация?
Я, в таком случае, не понимаю, как вы отделяете декларацию от инструкции.
IServiceCollection — это никоим образом не контейнер сервисов: интерфейс контейнера сервисов IServiceProvider в объекте, которыый его реализует, отсутствует, а потому получить оттуда реализацию сервиса для каких либо надобностей никак нельзя.
Он предтавляет собой всего лишь список деклараций, о том, что должно в будущем оказаться в в контейнере сервисов.
У нас с вами терминологическая разница. Я называю IServiceCollection
контейнером, а IServiceProvider
— провайдером. А вы называете первое декларацией, а второе контейнером.
Но суть от этого не меняется. (Прямые) операции над IServiceCollection
выполняются сразу же, а не отложено.
Предполагается, что DSL и средства работы с ним вам тоже предоставят. Это снимает ваше возражение?
Когда предоставят, тогда и снимет возражение. За весь мой опыт работы этого не произошло ни разу, сколько бы ни обещали.
Кстати, точно так же можно нарваться на несуществующую функцию, и безо всякого дополнительного языка — работая, к примеру, с ASP.NET MVC.
"Нарваться" можно на что угодно. Это не повод пренебрегать существующими инструментами.
А где вы там нашли декларативный подход?Там прямо в документации по LINQ написано: «Query expressions are written in a declarative query syntax.»
Я, в таком случае, не понимаю, как вы отделяете декларацию от инструкции.Хороший вопрос, с решения которого надо было бы, вообще-то, было начинать. Декларация, в моем понимании — это описание того, что нужно сделать, без детализации как сделать. И мне нравится определение декларативного программирования из английской Википедии: «a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow»
В данном случае это практически так и control flow в AddOptions() является чисто вспомогательным: он состоит в занесении отдельных деклараций (о том, какие сервисы будут доступны через IServiceProvider) в список этих деклараций (IServiceCollection), и нужен он там только из-за особенностей языка: иначе в C# сложно сделать создаваемый в несколько приемов список деклараций.
У нас с вами терминологическая разница.
Спасибо, что обратили внимание. Дальше, во избежание путаницы, буду использовать названия интерфейсов.
Но суть от этого не меняется. (Прямые) операции над IServiceCollection выполняются сразу же, а не отложено.
Операции над IServiceCollection — это операции над списком деклараций. В основном — простое линейное занесение в список, семантически это — полный аналог написания списка в файле. Кое-где — условное включение в список, но средства типичного декларативного языка сделать такое условное включение тоже обычно позволяют. То есть результатом всех этих операций является список деклараций. И особенные возможности императивного языка тут не используются, согласны?
Когда предоставят, тогда и снимет возражение.
ОК, значит вы не отвергаете возможность получния пользы от такого рода инструментов.
«Нарваться» можно на что угодно. Это не повод пренебрегать существующими инструментами.
Никто не предлагает вам пренебрегать. Наоборот, вам предлагают расширить круг инструментов. Возьмем тот же MVC — в его лице вы получили новый инструмент: динамическое определение вызываемой подпрограммы обработки запроса по соглашению об именах.
Там прямо в документации по LINQ написано: «Query expressions are written in a declarative query syntax.»
На заборе тоже пишут. А в реальности… Что декларативного в записи orders.Select(o => o.Amount > 100)
?
Декларация, в моем понимании — это описание того, что нужно сделать, без детализации как сделать.
Эмм. Вы считаете, что операция Add
на коллекции — это декларация, да?
Операции над IServiceCollection — это операции над списком деклараций.
Это просто операции над коллекцией. Что в ней — уже не важно. Они императивны по самое не знаю что.
А потом мы выполняем другую операцию над этой коллекцией и получаем третий объект — провайдер. Тоже императивная операция.
Выглядит как обычный паттерн builder.
В данном случае это практически так и control flow в AddOptions() является чисто вспомогательным
Я рекомендую вам задуматься, что порядок операций над IServiceCollection
имеет значение. Это вы называете "вспомогательным control flow"?
И особенные возможности императивного языка тут не используются, согласны?
Что такое "особенные возможности императивного языка"?
ОК, значит вы не отвергаете возможность получния пользы от такого рода инструментов.
Нет, не отвергаю. Я просто говорю, что сейчас их нет.
Наоборот, вам предлагают расширить круг инструментов.
Расширить — это когда мне предлагают что-то в дополнение. А тут мне предлагают отказаться от конфигурации через код, которая обладает определенными достоинствами, в обмен на конфигурацию через YAML, которая никакими объективными достоинствами в данной реализации не обладает. Не вижу никакого расширения.
Возьмем тот же MVC — в его лице вы получили новый инструмент: динамическое определение вызываемой подпрограммы обработки запроса по соглашению об именах.
Это не MVC, а роутинг.
На заборе тоже пишут. А в реальности… Что декларативного в записи orders.Select(o => o.Amount > 100)?
М-м, а вы точно знаете, как это будет выполнено? В частности, если orders реализует IQueryable<тип-о>, то для получения результата вместо банального перебора провайдером данных может быть выполнен эквивалентный запрос на другом языке запросов (например — на SQL): лямбда в C# может, как известно, быть преобразована в дерево выражения, а оно, в свою очередь — транслировано провайдером на нужный язык запросов. И выполнять ваш запрос будет, к примеру, SQL сервер, спрятавшийся за orders — а вы получите только результат. По-моему, это весьма декларативно: вы написали на C#, что вы хотели получить, а результат вам (внезапно) предоставил своим, более эффективным, способом понятливый ORM с SQL Server'ом на пару — хотя вы в момент написания лямбды, возможно, даже не задумывались об их существовании.
И, кстати, вы, наверное, имели в виду orders.Where(o => o.Amount > 100)? Иначе мне трудно предположить, зачем вам этот результат — IEnumerable<Boolean>, т.е., последовательность непонятных true и false. ;-)
Эмм. Вы считаете, что операция Add на коллекции — это декларация, да?В контексте стадии конфигурирования ASP.NET смысл вызова Add обычно — роль разделителя, типа перевода строки и пробелов в ней: так здесь принято оформлять строку конфигурации, операция Add самостоятельного смысла не несет, это boilerplate.
Это просто операции над коллекцией. Что в ней — уже не важно. Они императивны по самое не знаю что.
Вы зря сосредотачиваетесь на способе создания списка деклараций — он не важен. Имеет значение сам список, а способ его создания — это некий ритуал, комбинируемый из фиксированных и практически неизменных частей.
А потом мы выполняем другую операцию над этой коллекцией и получаем третий объект — провайдер. Тоже императивная операция.
Операция императивная, но мы эту операцию не выполняем. Она выполняется библиотекой где-то там за сценой, хотя и по нашей общей команде (которая тоже входит в ритуал). А прикладной программист на ASP.NET может даже не подозревать о существовании этой операции и, при этом, вполне успешно работать: он просто следует ритуалу — описывает конфигурацию, создает хост для веб-приложения командой Build и дальше запускает его одним из предусмотренных для этого способов. Короче, то, что надо программировать императивно — все уже запрограммировано до нас, и потому я предлагаю ограничиваться рассмотрением только тех операции, которые делает прикладной программист.
Расширить — это когда мне предлагают что-то в дополнение. А тут мне предлагают отказаться от конфигурации через код, которая обладает определенными достоинствами, в обмен на конфигурацию через YAML, которая никакими объективными достоинствами в данной реализации не обладает.
Я когда это писал, предполагал не ограничиваться рассмотрением фреймворка idewavecore (автор просит называть его так, см. ниже), а обсудить и принципиально возможные гипотетичекие решения на базе DSL-фреймворков: как я понял, вы от них принципиально откзывались. Но если вы отказываетесь от них не принципиально, а чисто в виду отсутсвия подходящих, и даже если вы вообще не верите в возможность существования подходящих, то тут дальше обсуждать нечего, за неимением предмета обсуждения.
Это не MVC, а роутинг.
Нет, это именно MVC. Не знаю, как точно это реализовано в Core (полагаю, что сходным образом), но в Framework вся магия (там есть много точек, в которые можно вмешаться) выбора класса контроллера, создания его экземпляра и последующего выбором метода действия начинается в обработчике маршрутов из состава MVC — MvcRouteHandler: именно его надо указывать при ручном создании объекта маршрута для приложения MVC.
А роутинг — это общий компонент, в контексте работы MVC он только разбирает путь в URL и выбирает обрабочик маршрутов.
М-м, а вы точно знаете, как это будет выполнено?
Нет. Точно так же, как я не знаю, как будет выполнено Logger.Log
или Service.Invoke
. Вот только это для меня называется "сокрытие информации" (иногда это же называют инкапсуляцией, но вокруг этого термина уже есть споры), а совсем не декларативным программированием.
Но если smth.Select(projection)
для вас декларативное программирование, то C# давно и прочно декларативный язык, и обсуждать действительно нечего.
Я когда это писал, предполагал не ограничиваться рассмотрением фреймворка idewavecore (автор просит называть его так, см. ниже), а обсудить и принципиально возможные гипотетичекие решения на базе DSL-фреймворков
Множество гипотетических решений на базе DSL-фреймворков бесконечно. Что конкретно там обсуждать, и, главное, зачем?
Нет, это именно MVC. Не знаю, как точно это реализовано в Core (полагаю, что сходным образом), но в Framework вся магия (там есть много точек, в которые можно вмешаться) выбора класса контроллера
Вы нечувственно перешли от MVC к ASP.NET MVC, хотя это слегка разные вещи.
Но даже в ASP.NET у меня есть "динамическое определение на основании соглашения" безо всякого MVC. Самописное, больше чем одним способом.
«фреймворк»
обсуждаемое то-что-должно-стать-фреймворком по факту ее решает «не слишком хорошо».Подобные высказывания — несправедливы по отношению к моему труду. Если вы видите недостатки — пожалуйста, прямо на них укажите, как это делает, к примеру, ваш собеседник (lair). Пожалуйста, обсуждайте недостатки, а не само явление — фреймворк. Без кавычек.
Единственное, что я могу сделать для вас — это называть ваш труд тем словом, которым вы желаете.
Скажите название вашего фреймворка, и я обещаю называть его в дальнейшем только так, как вы пожелали — если это, конечно, не приведет к путанице.
и мне здесь ему возразить нечегомне тоже нечего возразить. Но это означает только одно — мне нужно больше знаний и я понял, в каких областях. И я уже над этим работаю. Я постараюсь, чтобы новая версия idewavecore была безупречной.
Ну как минимум, если мне могут понадобиться разные варианты — мне придется держать пять папок с разными py-файлами. Либо одну папку — с пятью разными конфигами. Мне кажется, разница — есть.
А что вам мешает держать одну папку с пятью разными py-файлами?
Декларативное программирование однозначно вещь. nginx и swiftui как примеры.
Статья написана на горячем языке. Видно что Вы любите то, что делаете, так что не бросайте!
Мне кажется, использования декларативных языков в подобных целях, это путь в никуда. Как только появляется какая-либо небольшая сложность "сделать шаг влево", сразу вылезает эта ограниченность декларативного описания. И со временем становится непонятно, а зачем мы это сделали. Вроде никакую проблему не решили, зато привнесли новые.
Повторю совет выше, попробуйте сделать живые примеры применения своего фреймворка. Может быть будет все круто. А может быть, вылезет куча проблем.
Идея хорошая. Я в python не разбираюсь от слова совсем, поэтому скажу как это выглядит для меня с моими C# и C++. Во-первых, когда вы храните конфигурации типа "по какому адресу что запустить", то это поведение можно легко менять для уже работающей программы. Не нужно ничего перезапускать, перекомпилировать, и тд, а это весьма неплохое техническое преимущество. Во-вторых, это даёт возможность легко расширять работающее приложение. Пишите функционал, загоняете в dll, пишите в конфиг, а Ваш сервер динамически грузит dll и получает новую функциональность ( не знаю, как это в Python, но думаю похожий механизм есть). А если у Вас функции написаны так, что им все равно в каком контексте они исполняются ( депенденси инжекшн и тп), то их становится намного легче автоматизировано тестировать, так как сервер теперь выступает по сути маршрутизатором к независимым функциям, и в тесте самой функции он не нужен. Я сам где-то похожую штуку сделал для своего проекта (правда похожа только идея, реализации весьма разные). Но хочу посоветовать, не очень увлекаться конфигом. Он хорош для описания простого связывания "пришел такой запрос-дернули такую функцию", но как только появится желание описать там поведение посложнее (ветвления и тп), то стоит ооочень подумать. Все же для описания сложного поведения язык программирования подойдёт лучше))
Удачи в дальнейшем развитии)
Он хорош для описания простого связывания "пришел такой запрос-дернули такую функцию"
А зачем такое в конфиге? Я вот не могу сходу придумать, как этим пользоваться. У нас в коде есть несколько одинаковых функций и нам зачем-то надо периодически переключать функцию, которая обрабатывает роут? Был бы рад примеру для цитаты выше, правда
когда вы храните конфигурации типа "по какому адресу что запустить", то это поведение можно легко менять для уже работающей программы. Не нужно ничего перезапускать, перекомпилировать
Ну это очевидный тезис. Я, кажется, и не припомню программ, где бы "адрес прослушивания" например, был зашит в коде. Верно же?
В asp.net путь по которому будет доступен метод контроллера, обычно, или выводиться из имён класса и метода, или прописываются атрибутом на них. Иногда это конфигурируют в стартапа. Но вот именно поменять адрес в процессе работы… Я таких методов не знаю, хотя я и не эксперт в asp.net (наверное поэтому и не знаю, но что на что менять в любом случае должно быть где-то прописано). Основная моя идея была в том, что мы дописываем в конфиг ( хотя у меня в проекте оно все совсем по другому храниться) связь адрес-функция в дол, и сервер подтягивает эту длл. Становится легче добавлять новую функциональность. Да и заменить имеющуюся иногда нужно. И даже не обязательно она будет с тем же набором аргументов (api тоже меняются), дальше там уже можно разные дергать в зависимости от того, какая версия api вызывается, но это уже другая история. Плюс, почему это было удобно нам в проекте, так это из-за того, что длл в шарпе не так просто выгрузить и на ее место выгрузить ее новую версию. Раньше это делалось только через appDomain, в core их поддерживать перестали и теперь это через assemblyLoadContext, но и там все не так просто. Поэтому иногда для быстрого и эффективного решения проблемы легче выгрузить новую длл и поставить новый обработчик на роут. А потом можно все перезапустить и сделать по людски. У нас это росло в рамках более глобальной инфраструктуры которая давала больше интересных возможностей, но это уже не для комментария
Надеюсь в этот раз понятнее выразился, чем в предыдущем комментарии
Она всем нравится. Пока внезапно сервер не начинает вести себя совершенно непредсказуемым образом.
Не "неправильно", а "непредсказуемо". Потому что вам нужен live reload, и вам нужно отследить, что все ссылки на заменяемый объект умерли. Особенно это весело, если вы в этот момент обрабатываете запрос. А потом вы внезапно выясняете, что когда у вас в каком-нибудь хранилище, типа сессионного, данные положены старой версией, а вы их читаете новой, что-нибудь слегка идет не так. Не падает с ошибкой (а это было бы намного лучше), а просто трактует поле не как true
, как оно было, а как false
. И это ползет дальше, и дальше, и дальше вниз по системе.
Причем нет, я не говорю, что это все нерешаемо. Это вполне решаемо. Это просто много разнородных проблем, все из которых надо учесть, потратив на это приличное количество усилий и нервов, когда, возможно, для конкретной задачи перезапуск сервера — это не проблема. Или, возможно, можно перестроить архитектуру так, чтобы каждая "функция" была независимым микросервисом, и тогда их действительно можно заменять без затрагивания других, но это принципиально другой дизайн.
Каждое такое решение — это (почти всегда) компромис. И надо долго думать и взвешивать.
Поэтому иногда для быстрого и эффективного решения проблемы легче выгрузить новую длл и поставить новый обработчик на роут.
Удивительно, но конфиг для этого все еще не нужен. Нужен runtime discovery, и это, кстати, больно, потому что в asp.net core у вас обычно есть контейнер зависимостей, и он предполагается быть собранным в начале и навсегда, и менять его где-то между "дорого" и "невозможно". И вот эта инфраструктура — она настолько сложнее, собственно, конфига, что он пропадает из вида за ненадобностью.
Я с Вами согласен касательно зависимостей. Хочу сказать, что это можно решать, если не забиваться на реализацию и архитектуру из коробки, но это снова же другая тема. А касательно кофига, то Вам же в любом случае нужно где-то сохранить что, куда и как грузить. И будь это конфиг, или база данных, или любой другой источник данных, это абсолютно не принципиально. Это же по сути просты данные, которые хотя и используются для настройки сервера, но все равно остаются данными, а не кодом, скриптом и тп
Я с Вами согласен касательно зависимостей. Хочу сказать, что это можно решать, если не забиваться на реализацию и архитектуру из коробки, но это снова же другая тема.
Можно. Вопрос сложности и предсказуемости получившегося решения, особенно для вновь приходящих разработчиков.
Это же по сути просты данные, которые хотя и используются для настройки сервера, но все равно остаются данными, а не кодом, скриптом и тп
Никогда не слышали выражение infrastructure as a code?
А вот тут мы приходим к вопросу подходов. Да, когда есть пару скриптов которые все настраивают/конфигурируют — это хорошо и удобно. Но это удобно для сложных настроек, которые реально нуждаются во внутренней логике, а не просто описывают a=8;b=2; Но это все равно остаётся делом эстетического вкуса и практического удобства, которые по большей части зависят от самого разработчика ( субъективного его понимания мира) и решаемых задач ( объективной реальности)
Но это все равно остаётся делом эстетического вкуса и практического удобства
Еще это вопрос поставленных требований. Если вам надо раскатывать систему на много окружений, и эти окружения должны быть гарантированно идентичны, потому что иначе вы не можете обеспечить нужное качество в проде — автоматическая строго контролируемая накатка становится бесценной.
В качестве анекдота, мы с коллегами потратили значимую часть предыдущей недели на расследование бага, который в итоге оказался вызван тем, что контрагент не накатывал систему штатным инсталлером, а обновлял сборки вручную, и у него залип отладочный конфиг с fail-fast-флагом.
Еще один фреймворк…