Тогда нужно менять язык. Идея Go в простоте, а дженкрики никогда не были простыми - после введения базовых дженериков, начнётся форсинг ко/контр-вариантности, затем форсинг дженериков-полных-по-тьюрингу как в С++ (тут есть надежда, что фиче реквест отобьют, ибо уже слишком сложно)
TL/DR - добавив дженерики и т.п. - получится очередной .NET/C#, а простота присущая Go пропадёт - зачем это надо? Может проще сразу писать на условном .NET?
К тому же, сложение функций в стиле последовательного вызова - это не так чтоб очевидная операция. Я бы лично предпочёл синтаксическую ошибку в таком случае (вызывайте компоуз явно если действительно хотите их “сложить”). Т.е. «сложение» ивент хендлеров мне (лично) выглядит очевидным, а вот сложение «обычных» функций - нет. С#, к сожалению, не разделяет эти два юз кейса.
Нуу… вроде и да, но мне в отладчике не было понятно сходу, каким чудом вызвался Delegate.Compose - вроде бы и логично, но в коде такого нету, без анализа компилятора неочевидно почему ИМЕННО ЭТОТ вызов был сделан
Если честно, я не очень понял как это работает в Котлин.
Вот пример: допустим у нас есть класс Service1, который создаётся IoC контейнером, он зависит от логгера, который инжектится тем же IOC контейнером, в с# я могу написать как-то так:
class Service1 {
[Inject]
public ILogger Logger { get; set; } = null!;
public void DoSomething() {
// …
Logger.Info(“test”); // Logger is not-null
}
}
Здесь конструкция «= null!» говорит компилятору, что я хорошо подумал, и Logger будет гарантированно инициализирован при создании объекта. Как тоже самое сделать в Котлин?
Да, вы правы - я как-то привык ходить в декомилированную реализацию для последних новшеств типа асинк и уже подзабыл что многие другие вещи объявляются неявно.
Иными словами, мне хотелось бы видеть определение Func не как делегат сам по себе, а как обертку над делегатом, которая показывает, что именное происходит внутри. Так, к примеру, делают в с++ - вы можете посмотреть что внутри std::function или std::string - мне почему-то казалось, что с# пошёл похожим путём, но это (очевидно) когнитивная иллюзия.
Костыль в том, что это преобразование объявлено неявно. Если бы тип Func<T> имел статический метод Func<T> operator +(Func<T> a, Func<T> b) - то было бы более менее логично - мы просто вызываем оператор. Но такого метода нету (его не выдаёт рефлексия, его не показывает ILDasm).
Очень похоже что этот кхм «метод» встроен в сам компилятор - компилятор «знает из коробки» что если пытаются сложить две функции одинакового типа, то надо вызвать Delegate.Combine и скастить результат обратно в функцию - именно это знание я и называю костылём - оно неочевидное и его не видно в исходниках рантайма. Зато, я, кажется, нашёл это место в компиляторе: https://github.com/dotnet/roslyn/search?q=System_Delegate__Combine (файл LocalRewriter_BinaryOperator.cs, строки 175 и 230)
Вообще это очень интересная магия, если посмотреть IL код, видно что компилятор делает следующее:
Func<int> z = (Func<int>) (Delegate) Delegate.Combine(x, y);
Это очень похоже на неудачно выстреливающий костыль в компиляторе: очень похожие вещи происходят в event-ах, но там все выглядит логично из-за наличия в определении ключевого слова ивент которое явно говорит что «сложение» означает последовательный вызов обработчиков:
public event Func<int> Z;
// …
Z = x; // подписать на ивент X
Z += y; // а ещё подписать Y
Похоже что компилятор де-факто не проверяет наличия «event» в определении z - поэтому синтаксис из примера и работает. Интересно было бы копнуть глубже и посмотреть как Roslyn делает парсинг, но лень :)
Панельки нужны для редактирования и анализа структуры мокапа. Дело не в том, что их можно или нельзя убрать; дело не в отсутствие предпросмотра. Дело в том, что когда мокап в темных тонах, а его структура на панельках в белых тонах, то глаза у некоторых людей (типа меня) начинают ощутимо болеть/уставать от высокого контраста на экране.
Вам повезло - вы, похоже, успешно выделили и поддерживаете отдельные сущности/модули внутри вашего монолита. Если вы перечитаете мой первый комментарий, то увидите, что я писал о том, что это (в целом) возможно, но удаётся далеко не всем.
Начиная с микросервисов сразу мы отсрачиваем запуск проекта в целом.
Именно поэтому я добавил в конце оговорку про «умеют готовить devops» - если у команды есть опыт в этой сфере, то накладные расходы будут приемлемыми. Если опыта нет, то вы правы и надо пилить как получается в надежде получить опыт и прибыль от MVP; однако, надо помнить что в будущем таки придётся переделывать, если стартап взлетит; дальнейшие соображения зависят от специфики продукта, количества инвестиций и т.п.
Проектам редко нужно 50 человек. Скорее всего эти 50 пилят 5 разных проектов. Смежных, но разных.
Склонен с вами не согласится - «типичным магазинам» не нужен, а вот успешный энтерпрайз-грейд SaaS стартап - это как раз и есть человек 50, при этом все работают над одним продуктом (но над разными фичами). В таких ситуациях микросервисы являются той самой штукой, которая делает отдельные команды независимыми при условии правильного разделения зон ответственности.
Вы с одной стороны правы (микросервисы, действительно не решают проблему горизонтального масштабирования нагрузки), но приходите к «опасным» выводам. Основная выгода от микросервисов проявляется, когда ваша команда становится достаточно большой, чтобы проявлялись проблемы единой кодовой базы (монолита). 10 человек могут работать с монолитом эффективно, 50 - уже не могут: появляются мерж-конфликты, регрессивное тестирование становится дорогим, а релизы - праздником, а не повседневностью как должно быть. Монолит (в смысле деплоя) - это такая штука, которую вроде бы и можно сделать «правильно» - выделив и изолировав модули, - но она не обеспечивает надлежащего контроля над границами этих самых модулей; в условиях «релиз надо на вчера» (как обычно бывает), монолит склонен к скатыватнию в спагетти-код, где всё зависит от всего. Именно поэтому стоит начинать с микросервисов сразу - не дожидаясь пока у вас накопится тонна неподдерживаемого кода. Для этого, конечно, нужна команда, которая умеет правильно готовить devops часть, ну а технологий, сегодня, (благо) хватает, велосипед изобретать не нужно.
Даже не знаю, фигма это отдельное зло. Как можно всерьёз говорить про дизайнер мокапов у которого, банально, нету темной цветовой темы? Редактировать/смотреть на мокап в темных цветах на фоне белых панелек, которые нельзя убрать - то ещё удовольствие для глаз.
Интерфейс фигмы можно назвать простым и понятным, но никак не удобным.
Там даже нету нормальной палитры цветов. Хочешь градиент от красного к зелёному в «правильном» цветовом пространстве, которое сохраняет визуальную яркость цвета воспринимаемую человеком (типа lab)? Ищи в интернете генератор и копируй цвета обратно в фигму вручную :(
Есть такие задачки, до сих пор помню code challenge 15-летней давности, который меня выбил на неделю
язык: asm
набор инструкций: I 386**
цель: написать DOS программу (.com), которая принимает на стандартный вход 1 байт (число 0-255) и выводит в стандартный вывод N знаков $, где N - число на входе, после чего завершается с кодом 0
критерий оценивания: побеждает программа минимального размера
** ну, и маленький нюанс: нельзя использовать команды условного перехода (jz, je, jne и т.п.), нельзя использовать арифметику (add, sub, inc, dec, mul, div), в том числе адресную арифметику (lea, dword ptr [eax + 4] и т.п.)
Маки живут 5 лет легко как новые, наверное и 10 живут, но дольше 5 лет все равно нет смысла держать ноут (разве что, в режиме печатаной машинки). Моему старому эйру 9 лет и до он сих пор работает и, даже, обновляется.
Желтоватенько, конечно. Если errno определена препроцессором, значит ее реализация «встраивается» в бинарники во время компиляции, так ведь? То есть все уже собранные бинарники перестанут работать корректно если реализацию поменять.
Вопрос: какого масштаба апокалипсис должны найти в текущей реализации чтобы такое изменение протащить через все код ревью в прод? Сломавшийся при этом GO будет явно меньшей проблемой, да и ту можно починить «скопировав» реализацию ещё раз и все пересобрав.
Тогда нужно менять язык. Идея Go в простоте, а дженкрики никогда не были простыми - после введения базовых дженериков, начнётся форсинг ко/контр-вариантности, затем форсинг дженериков-полных-по-тьюрингу как в С++ (тут есть надежда, что фиче реквест отобьют, ибо уже слишком сложно)
TL/DR - добавив дженерики и т.п. - получится очередной .NET/C#, а простота присущая Go пропадёт - зачем это надо? Может проще сразу писать на условном .NET?
За оторванность от суровой реальности, очевидно :)
К тому же, сложение функций в стиле последовательного вызова - это не так чтоб очевидная операция. Я бы лично предпочёл синтаксическую ошибку в таком случае (вызывайте компоуз явно если действительно хотите их “сложить”). Т.е. «сложение» ивент хендлеров мне (лично) выглядит очевидным, а вот сложение «обычных» функций - нет. С#, к сожалению, не разделяет эти два юз кейса.
В условном С++ тот же оператор сложения строк определён явно - если вы отлаживаете дебаг версию к нему можно перейти поднявшись по стеку
Нуу… вроде и да, но мне в отладчике не было понятно сходу, каким чудом вызвался Delegate.Compose - вроде бы и логично, но в коде такого нету, без анализа компилятора неочевидно почему ИМЕННО ЭТОТ вызов был сделан
Если честно, я не очень понял как это работает в Котлин.
Вот пример: допустим у нас есть класс Service1, который создаётся IoC контейнером, он зависит от логгера, который инжектится тем же IOC контейнером, в с# я могу написать как-то так:
class Service1 {
[Inject]
public ILogger Logger { get; set; } = null!;
public void DoSomething() {
// …
Logger.Info(“test”); // Logger is not-null
}
}
Здесь конструкция «= null!» говорит компилятору, что я хорошо подумал, и Logger будет гарантированно инициализирован при создании объекта. Как тоже самое сделать в Котлин?
Да, вы правы - я как-то привык ходить в декомилированную реализацию для последних новшеств типа асинк и уже подзабыл что многие другие вещи объявляются неявно.
Иными словами, мне хотелось бы видеть определение Func не как делегат сам по себе, а как обертку над делегатом, которая показывает, что именное происходит внутри. Так, к примеру, делают в с++ - вы можете посмотреть что внутри std::function или std::string - мне почему-то казалось, что с# пошёл похожим путём, но это (очевидно) когнитивная иллюзия.
Костыль в том, что это преобразование объявлено неявно. Если бы тип Func<T> имел статический метод Func<T> operator +(Func<T> a, Func<T> b) - то было бы более менее логично - мы просто вызываем оператор. Но такого метода нету (его не выдаёт рефлексия, его не показывает ILDasm).
Очень похоже что этот кхм «метод» встроен в сам компилятор - компилятор «знает из коробки» что если пытаются сложить две функции одинакового типа, то надо вызвать Delegate.Combine и скастить результат обратно в функцию - именно это знание я и называю костылём - оно неочевидное и его не видно в исходниках рантайма. Зато, я, кажется, нашёл это место в компиляторе: https://github.com/dotnet/roslyn/search?q=System_Delegate__Combine (файл LocalRewriter_BinaryOperator.cs, строки 175 и 230)
Вообще это очень интересная магия, если посмотреть IL код, видно что компилятор делает следующее:
Func<int> z = (Func<int>) (Delegate) Delegate.Combine(x, y);
Это очень похоже на неудачно выстреливающий костыль в компиляторе: очень похожие вещи происходят в event-ах, но там все выглядит логично из-за наличия в определении ключевого слова ивент которое явно говорит что «сложение» означает последовательный вызов обработчиков:
public event Func<int> Z;
// …
Z = x; // подписать на ивент X
Z += y; // а ещё подписать Y
Похоже что компилятор де-факто не проверяет наличия «event» в определении z - поэтому синтаксис из примера и работает. Интересно было бы копнуть глубже и посмотреть как Roslyn делает парсинг, но лень :)
Панельки нужны для редактирования и анализа структуры мокапа. Дело не в том, что их можно или нельзя убрать; дело не в отсутствие предпросмотра. Дело в том, что когда мокап в темных тонах, а его структура на панельках в белых тонах, то глаза у некоторых людей (типа меня) начинают ощутимо болеть/уставать от высокого контраста на экране.
Вам повезло - вы, похоже, успешно выделили и поддерживаете отдельные сущности/модули внутри вашего монолита. Если вы перечитаете мой первый комментарий, то увидите, что я писал о том, что это (в целом) возможно, но удаётся далеко не всем.
Именно поэтому я добавил в конце оговорку про «умеют готовить devops» - если у команды есть опыт в этой сфере, то накладные расходы будут приемлемыми. Если опыта нет, то вы правы и надо пилить как получается в надежде получить опыт и прибыль от MVP; однако, надо помнить что в будущем таки придётся переделывать, если стартап взлетит; дальнейшие соображения зависят от специфики продукта, количества инвестиций и т.п.
Склонен с вами не согласится - «типичным магазинам» не нужен, а вот успешный энтерпрайз-грейд SaaS стартап - это как раз и есть человек 50, при этом все работают над одним продуктом (но над разными фичами). В таких ситуациях микросервисы являются той самой штукой, которая делает отдельные команды независимыми при условии правильного разделения зон ответственности.
del
Вы с одной стороны правы (микросервисы, действительно не решают проблему горизонтального масштабирования нагрузки), но приходите к «опасным» выводам. Основная выгода от микросервисов проявляется, когда ваша команда становится достаточно большой, чтобы проявлялись проблемы единой кодовой базы (монолита). 10 человек могут работать с монолитом эффективно, 50 - уже не могут: появляются мерж-конфликты, регрессивное тестирование становится дорогим, а релизы - праздником, а не повседневностью как должно быть. Монолит (в смысле деплоя) - это такая штука, которую вроде бы и можно сделать «правильно» - выделив и изолировав модули, - но она не обеспечивает надлежащего контроля над границами этих самых модулей; в условиях «релиз надо на вчера» (как обычно бывает), монолит склонен к скатыватнию в спагетти-код, где всё зависит от всего. Именно поэтому стоит начинать с микросервисов сразу - не дожидаясь пока у вас накопится тонна неподдерживаемого кода. Для этого, конечно, нужна команда, которая умеет правильно готовить devops часть, ну а технологий, сегодня, (благо) хватает, велосипед изобретать не нужно.
Даже не знаю, фигма это отдельное зло. Как можно всерьёз говорить про дизайнер мокапов у которого, банально, нету темной цветовой темы? Редактировать/смотреть на мокап в темных цветах на фоне белых панелек, которые нельзя убрать - то ещё удовольствие для глаз.
Интерфейс фигмы можно назвать простым и понятным, но никак не удобным.
Там даже нету нормальной палитры цветов. Хочешь градиент от красного к зелёному в «правильном» цветовом пространстве, которое сохраняет визуальную яркость цвета воспринимаемую человеком (типа lab)? Ищи в интернете генератор и копируй цвета обратно в фигму вручную :(
Не помню точно условие (да и размер победителя), к сожалению, помню только что «тривиального» решения там не было
Есть такие задачки, до сих пор помню code challenge 15-летней давности, который меня выбил на неделю
язык: asm
набор инструкций: I 386**
цель: написать DOS программу (.com), которая принимает на стандартный вход 1 байт (число 0-255) и выводит в стандартный вывод N знаков $, где N - число на входе, после чего завершается с кодом 0
критерий оценивания: побеждает программа минимального размера
** ну, и маленький нюанс: нельзя использовать команды условного перехода (jz, je, jne и т.п.), нельзя использовать арифметику (add, sub, inc, dec, mul, div), в том числе адресную арифметику (lea, dword ptr [eax + 4] и т.п.)
Победила программа размером (кажется) 19 байт
Маки живут 5 лет легко как новые, наверное и 10 живут, но дольше 5 лет все равно нет смысла держать ноут (разве что, в режиме печатаной машинки). Моему старому эйру 9 лет и до он сих пор работает и, даже, обновляется.
Ах да, .h файлы - это вполне себе documentation-as-a-code в таких случаях. Даже с комментариями иногда.
Желтоватенько, конечно. Если errno определена препроцессором, значит ее реализация «встраивается» в бинарники во время компиляции, так ведь? То есть все уже собранные бинарники перестанут работать корректно если реализацию поменять.
Вопрос: какого масштаба апокалипсис должны найти в текущей реализации чтобы такое изменение протащить через все код ревью в прод? Сломавшийся при этом GO будет явно меньшей проблемой, да и ту можно починить «скопировав» реализацию ещё раз и все пересобрав.