Comments 36
И вы до сих пор думаете, что С# и Java не были ошибками? Настолько сложные навороты вокруг абсолютно элементарной вещи, которая должна писаться в одну строку. А тут ещё и оверхед на рантайме огромный
Навороты конечно непростые, но про оверхед вы не правы. Эти "аппендеры" являются структурами нулевого размера, которые используются как типы-параметры в дженерик методе, тут нет никаких аллокаций и кастов и, скорее всего, весь код "аппендеров" будет заинлайнен JIT'ом.
JIT это значит нужно на рантайме что то инлайнить куда то
Может, сможете предложить хорошую замену? :D
C++, то что пишется в статье выглядит там просто как
a + b
И никакого джита и рантайм оверхеда
То есть, вместо "jit оверхеда" предлагается язык, созданный для отстрела ног? Спс, но я лучше перейду с C# на Python, чем на это недоразумение из 80-х, даже создатель которого рекомендует писать на чем-то другом. Или останусь в мире оверхеда, зато с GC и отсутствием 10 способов выделить и освободить память, половина из которых объявлена ересью.
ну тут я даже ничего говорить не буду, бессмысленно, пишите с гц, джитом, абстрактными фабриками абстрактных фабрик, мне до этого дела особо нет, главное чтобы я не этим занимался
Ничего говорить не будешь, просто потому что тебе нечего сказать. Зачем ты тогда сюда зашёл? Чтоб неконструктивный срач устроить?
Кстати, судя по комментариям в других топиках, ты любитель пополивать другие языки, особенно языки с gc (кстати, почему у тебя так сильно с gc горит?). Неадекват одним словом.
я хоть на C++ и не пишу уже лет 8, но меня удивляет, как джависты/шарписты, которые имеют представление о C++ на уровне универских лаб, цепляются к этой памяти и своему GC, как будто других ресурсов, типа сокетов, файловых дескрипторов итд, у них нет. Лет 100 уже в плюсах никто памятью вручную не рулит, есть RAII. Даже в Scala typeclass-ы используются, а в плюсах есть такая вещь как концепты. Плюсы вообще самый гибкий язык со статической типизацией из тех, с которыми я работал, достаточно почитать Александреску "Как я поел грибов современное проектирование на C++"
Вчера настроения не было писать, но да, однажды я программисту на Java рассказал как на самом деле устроено "управление памятью" в С++(просто как работают деструкторы и область видимости) и он не мог поверить, что вот так вот просто можно выкинуть гц, а ещё менеджить огромное количество других ресурсов с помощью этого механизма
А потом эти же программисты учат все реализации гц, чтобы знать это и учитывать, чтобы код выполнялся за конечное время и пишут int a,b вместо int a; int b, потому что так лучше оптимизирует jit.
Вообще это громадный миф, что у jit есть какая то там информация которая ему что-то позволяет лучше оптимизировать, фактически java/C# и тд неоптимизируемые языки силу того как они работают, если посмотреть на то что компилятор делает с С++ кодом конечно
Можно долбиться в байты, а можно делать дела и зарабатывать. За много лет .NET ни разу не подвёл, никаких проблем с GC не видывал. По удобству разработки и инструментария топ.
Извините "дрочить" на байты и процессорные тики, это отдельный узкий профиль. Как говорится, если действительно понадобится Си, или С++, да хоть брейнфак -- возьмём его.
Ох уж этот религиозный фанатизм GC/не-GC, ой да там RAII, ой а у .NET-а рефлексия и аспекты, и т.д. и т.п.
Я точно знаю одно. Разработка большого проекта аля учётная система, на С++ будет дороже на порядок, ещё и дольше. А будет ли ощутимый выигрыш? Я очень сомневаюсь, так как .NET ещё не был узким местом в моей практике нигде.
Это очередной миф, что в С++ обязательно надо долбиться в байты. Не надо.
На С++ сложнее научиться писать, но после того как умеешь - разработка ни разу не медленнее чем на любом другом языке, а может и быстрее, язык позволяет добиться любого уровня абстракции, в этом плане он гибче и си шарпа и джавы и питона и тд
Правда в том, что другие языки нужны не разработчикам, а компаниям, чтобы меньше денег вкладывать в обучение людей, набрать по скидке и бацать микросервисы
А вы и правда считаете, что в мире инженеры тупые и им легко навязать какой-то фиговый инструмент? :)
Если бы на С++ разработка стоила бы дешево и позволяла достигать нужного уровня эффективности, я вас уверяю -- сегодня бы большинство писало на C++, заливать про тайные заговоры компаний не нужно. Тот же Microsoft вкладывается в разработку C++ постоянно, инструменты на высочайшем уровне. И что? Это очень хороший и уважаемый язык, но у него своя ниша.
Что касается гибкости. Копнуть можно куда угодно, и развивать холивор до бесконечности. Но давайте возьмём один самый банальный пример.
Нужно вот такое, очень много и часто:
public record SomeData(double? Cost, int? Amount,...)
std::optional? Передавать какую-нибудь фигню типа -1 как отсутствие значения? Нет уж, увольте. Сами кушайте :)
Судя по фразам, легко делается вывод, что вы засели в своем cpp-болотце. Ой нехорошо, надо гибче быть и объективно сравнивать. А так как компетенций нет (в контексте огромных .NET-овских пластов знаний), то лучше избегать фанатизма.
У плюсовиков это пассивный навык - не считать других разработчиков за людей, так как они выстрадали свои годы опыта.)
в современных реализациях вышеупомянутых платформ JIT развернёт это как надо и вполне может быть что заинлайнит вызов метода.
Какая-то неловкая статья о том, как присобачить дженерики туда, куда их не надо присобачивать. Очень наркомански выглядит метод класса, который принимает два параметра типа Т, чтобы склеить их в тот же тип Т.
Ладно, если использовать экстеншены, то да, на вход приходит два аргумента, но первый помечен ключевым словом this, а значит его вызов будет с одним аргументом.
В реальном коде склейки, плюсы, аппенды и прочие конкатинации работают в типе класса и принимают ОДИН аргумент типа класса и выдают свой же тип как результат. Зачем тут дженерик? Чтобы клеить все и вся не учитывая тип? Таких задач в жизни не бывает.
Ближе всего я вижу необходимость помечать классы как способные агреггироваться. Но в данном случае решение унаследоваться от абстрактного класса - это говёное решение. В реальной жизни классу уже есть от чего наследоваться, а для обозначения возможностей классов придумали интерфейсы. В таком случае не надо вот этих всех "мудростей", просто наследуем IPohooy<T> и реализуем метод интерфейса T Pohooy(T second). Всё
¯\_(ツ)_/¯
А почему не подходит такой вариант?
class Appender
{
public T AppendItems<T>(T a, T b) where T : IAppendable<T> => a.Append(b);
public int AppendItems(int a, int b) => a + b;
public string AppendItems(string a, string b) => a + b;
}
Вместо того, чтобы создавать объект нужной реализации IAppendable через default, можно же просто передать функцию append для нужных типов.
Каких именно типов? - Их может быть очень много.
Задача - иметь один интерфейс для конкатенации произвольных типов. А не городить по новой перегрузке на каждый тип.
Имею в виду вот такое
class Appender
{
public T AppendItems<T>(T a, T b, Func<T, T, T> append) => append(a,b)
}
Т.е. вместо структуры с методом писать функцию и её передавать.
Ну если клиентский код может предоставить метод, который выполняет конкатенацию (Func<T,T,T>), то зачем нужен Appender класс? Это уже бесполезная игра с кодом.
API должен выглядеть просто, несмотря на то, что там будет 100500 потомков, реализующих Append.
Если клиентский код может предоставить структуру с методом, то может предоставить и метод)
Вообще в ООП мире это называется паттерн стратегия или policy, тут ничего нового.
Это все верно, но только потому, что в итоге получилась такая реализация и тип операции передается явно, поэтому в многих случаях этот код можно заменить на более привычные вещи. Полноценно это можно сделать сейчас только для своих типов с помощью static abstract методов в интерфейсах, тогда достаточно указать что T : IAppendable<T>.
Конечно, есть вариант решить задачу «в лоб» — писать перегрузку на каждый тип данных, однако хотелось бы заставить компилятор помогать нам, не создавая 100500 методов.
Т.е. вместо 100500 методов вы предлагаете создать 100500 реализаций абстрактного класса? В чем профит?
Спасибо за статью.
> Конечно, есть вариант решить задачу «в лоб» — писать перегрузку на
каждый тип данных, однако хотелось бы заставить компилятор помогать нам,
не создавая 100500 методов.
Но в разве нам тогда не нужно будет создавать 100500 классв AppendableTYPE
?
Пока не могу понять, когда какой полиморфизм лучше использовать.
Для таких задач в современном дотнете есть Generic Math, например операция сложения
Да, прекрасно знаю об этом, однако прикрепил в конец статьи proposals, которые некоторые рассматривали как альтернативу для решения задачи обобщения алгоритмов, в том числе в обсуждении Generic Math в GitHub.
К сожалению, при ФП подходе придётся крафтить что-то вроде Scala implicits, а этого никто в здравом уме конечно делать не будет.
Поэтому, для C# был избран другой путь, как мне кажется более подходящий.
Так как позднего связывания здесь не может быть, то можно всё переписать на статику:
Console.WriteLine(Appender.AppendItems<AppendableInt, int>(1, 2));
Console.WriteLine(Appender.AppendItems<AppendableString, string>("1", "2"));
interface IAppendable<T>
{
abstract static T Append(T a, T b);
}
struct AppendableInt : IAppendable<int>
{
public static int Append(int a, int b) =>
a + b;
}
struct AppendableString : IAppendable<string>
{
public static string Append(string a, string b) =>
$"{a}{b}";
}
static class Appender
{
public static T AppendItems<TAppendable, T>(T a, T b)
where TAppendable : IAppendable<T> =>
TAppendable.Append(a, b);
}
Окей, полугруппа есть, моноид считай есть. Функтор тоже понятно как сделать. А аппликативный функтор получится?
Ad-hoc-полиморфизм и паттерн type class в C#