Как стать автором
Обновить

Комментарии 193

Интересно, почему «явные обобщённые лямбда-функции» — это серьёзно, а совместимость с C99 (на том же уровне, чистый синтаксический «сахар» — но весьма и весьма удобный, позволяющий, в частности, вызывать функции «почти что с именованными параметрами») — нет…
НЛО прилетело и опубликовало эту надпись здесь
Как мы без этого жили.
НЛО прилетело и опубликовало эту надпись здесь
Если честно, я еще 3 года назад жил без него :(

Что будет совершенно не нужно при появлении концептов :)

НЛО прилетело и опубликовало эту надпись здесь
Тогда уж
[](Sortable x) requires Orderable<decltype(x)> { ... }

Хотя Sortable — это подмножество Orderable и тогда requires не нужен
НЛО прилетело и опубликовало эту надпись здесь
Да там много «но». Например, концепты появятся в стандарте вместе с explicit generic lambdas, и можно будет писать так:
[] <typename T> (T x) requires Sortable<T> && Orderable<T> { ... }

Плюс, уже было подмечено:
Что будет совершенно не нужно при появлении концептов :)

Ограничивать лямбды концептами как правило незачем. Они всё равно используются локально. А SFINAE делается и без лямбд
НЛО прилетело и опубликовало эту надпись здесь

Обсуждался синтаксис [](Sortable x) {}. Если условие составное, как у вас, то можно создать новый концепт. Но в целом да, это адекватный пример.

Улучшенная дедукция аргумента шаблона
vector v{vector{1, 2}};
// Выведет vector<int> вместо vector<vector<int>>

Вот это, честно говоря, очень странно. Если бы мне нужен был

vector<int>
, я бы так и написал vector v{1, 2}. Как тогда сделать
vector<vector>
? Какой-то геморрой на ровном месте.
Согласен, так себе улучшение, даже больше ухудшение. Мне в паре мест как раз надо вектор векторов передавать, я уже был в предвкушении 17 стандарта, а тут такой облом)
Хм, а мне пришла в голову идея, может в таком случае писать
vector<vector<>> a { {1,2} };

?

А если мы хотим сделать вектор из вектора? vector v{vector{1, 2}} интуитивно эквивалентен vector v = vector{1, 2}.

Кстати да, вот у вектора отличаются конструкторы () и {}, пусть для
{ vector{1,2} } выведется вектор векторов, а для ( vecotr {1, 2} ) — копия исходного вектора, так мне кажется логичнее будет.
интуитивно, запись vector v = {vecror{1,2}}; должна создавать вектор векторов.
Почитал «2D Graphics v1» предложение, надеюсь что это никогда не войдет в стандарт :) А что, давайте еще «кросплатформенный API по проигрыванию звука», «по декодированию видео», «по обучению нейронных сетей» — тоже тащить в стандарт языка. Прочитал Motivation и вот совсем не вдохновился. Другие низкоуровневые языки никак не стандартизируют работу с графикой, и им это никак не мешает.
НЛО прилетело и опубликовало эту надпись здесь

Бл*! Столько лет прошло, а до сих пор никто даже и не планирует добавить в стандарт C++ нормальную работу со строками с поддержкой UNICODE.

А смысл? Если их просто хранить и передавать нужно, то в std::string всё отлично вписывается в utf-8. А если обрабатывать — так ICU уже тоже есть и, как бы, никак от включения в стандарт не выиграет…

utf-8 прекрасно хранится в обычных строках (специально же эту кодировку так проектировали)
Для более "широких" кодировок есть wchar_t, char16_t и char32_t

Спасибо!

назовите хотя бы один яп с нормальной поддержкой строк
А что значит «нормальная» и «ненормальная»? Чем, например, ненормальная поддержка строк у JS?
у них очень неконсистентный апи (да и в целом в языке). Например, replaceAll реализуется через split + join, remove — через replace, размер строки — только в символьных позициях (а не байтах utf-8, к примеру)
НЛО прилетело и опубликовало эту надпись здесь
Вы уверены что она нормальная? Размер символа, до ECMA Script 6, был два байта, чего достаточно только для представления символов UNICODE 0-65536 (0 — CodePage). В ECMA Script 6 был введен целый новый зоопарк костылей методов для работы с CodePoint-ами из расширенного диапазона. А старые методы теперь перемещаются по CodeUnit-ам (читай кускам полных символов). Весть тот же геморой что и в С++ присутствует и там, с тем лишь осложнением, что у вас нет выбора размера CodeUnit-a и нет UTF-8, сомого оптимального представления UNICODE, на мой взгляд.
Я не очень разбираюсь, потому и спросил. Я помню, в 4-м пхп была отвратная работа с Уникодом, приходилось извращаться. В JS никогда об этом не задумывался, потому и решил, что в нем норм все)

В ECMA Script 6 был введен целый новый зоопарк костылей методов

Это каких?
«Костыли» — я употребил в том смысле, что, фактически, появляется новый набор методов, который с переменным успехом пытается исправить поддержку Юникода в JS. Например свойство length выдает не размер в символах, а размер в code unit-ах (16 битных). charAt() принимает индекс в code unit-ах и легко может попасть на середину code point-a и вернуть неверный код. На замену ему введен метод codePointAt(), который выдает целый code-point (учитывая суррогатные пары), но вот индекс по прежнему задается в code unit-ах. Для итерации по code point-ам теперь предлагается делать так:
const cnt = str.length;

for (const i = 0; i < cnt; ++i)  {

    const ch = str.codePointAt(i);
    // ...
    if (str.charAt(i) != ch)
        ++i;
}

Понятно, почему так получается, но как-то это далеко от элегантности, на мой взгляд.
Также место fromCharCode() появился fromCodePoint(), а учитывая как «много» людей «понимают» как устроен UNICODE и различные кодировки, думаю ясности это не прибавит.

Rust?

Вместо того чтобы довести плюсы до красивого, лаконичного и самое главное понятного языка, лепят очередные обертки над обертками оберток…
Да пускай лепят. И даже чем больше тем лучше, интересно же когда что-то новенькое… Я давно уже рассматриваю это не как идеал, а лишь как одну из экспериментальных площадок, на которой можно в том числе выяснить «как не надо делать». А в практическом программировании то что не нравится вполне можно обходить стороной:)
По хорошему давно уже надо создавать новый язык «с нуля» на замену С++. С учетом опыта не только С++, но и скажем C#, Java, Objective C, D, Go, Rust, Swift и некоторых других.
А чем D плох как «язык с нуля на замену С++»?
Да тоже неидеален. Мне не понравилось например отсутствие явных пространств имен/модулей (как в C++/C#). Вместо этого сделана дурацкая привязка имени файла к имени модуля как в Java.
Вы же понимаете, что это дело вкуса, уверен есть сторонники каждого из решений.
Например вот этим:
atomic!"+=" (*value, 1)
А ещё const, immutable, in — разные, но так похожи.
Да, ещё enum забыл.
Простите, не знаю ни С++ ни D, просто любопытствую. Объясните, пожалуйста, подробнее
enum — объект-константа, который совсем-совсем константа, то есть в течение времени жизни не может измениться;
immutable, const — это разные степени гарантий неизменяемости типа,
in может писаться с аргументом функции и тоже ещё одна гарантия неизменяемости, но уже аргумента функции.
Все они имеют разную силу, и потребность в такой градации мне кажется избыточной.
Ну immutable и const ведь совершенно о разных вещах! const говорит о самой переменной, а immutable — о том, что в ней.

А как сюда енум приплести — я вообще не представляю. Еще огурцов и темной материи не хватает.

Все они имеют разную силу, и потребность в такой градации мне кажется избыточной.

Это не градация, это разные вещи. Вот стол твердый, деревянный и тяжелый. Это ведь не градации одного понятия, а разные понятия. Так и тут.
Правильно, о разном, но весьма похоже. Как по мне — излишне.
Ничего излишнего тут нет.
Мы можем иметь константную функцию, то есть функцию, в работе которой не изменяются члены класса. И можем иметь мутабельную переменную, то есть такую переменную, которую мы можем изменить в константной функции. Это крайне полезная фича для всяких счетчиков, каких-либо флагов объекта, мьютексов и т.д., иначе бы мы просто не смогли пользоваться константными функциями.

Так что вам правильно сказали, вы сравниваете теплое с мягким.
Правильно, о разном, но весьма похоже. Как по мне — излишне

Они настолько же похожи как «деревянный» и «тяжелый», какой из двух — лишний?

В D сборщик мусора вместо RAII.

Я думал, там можно и сборщиком пользоваться и классической ручной уборкой.
Без сборщика мусора можно только если стандартную библиотеку не использовать. Кто будет это заниматься? Разработчики ядра ОС? Ну таких пока вроде не нашлось…

RAII лучше, чем ручная уборка.

Без механизма как в Rust RAII никаким образом не замена GC

без какого именно механизма «как в Rust»?

Ну, очевидно, владения и времени жизни.

«механизм владения и времени жизни» и есть RAII

Ну насколько я понимаю, RAII говорит про инициализацию ресурса, а не про его дальнейшее использование. Например, я хочу создать новый ресурс и вернуть его из функции. Разрушать его нельзя. При этом RAII работает: получая ресурс я его инициализировал и вернул функции выше. Про момент его уничтожения и очистки тут ничего не сказано. Хотя в доке вроде сказано что-то в про уничтожение


RAII guarantees that the resource is available to any function that may access the object (resource availability is a class invariant, eliminating redundant runtime tests). It also guarantees that all resources are released when the lifetime of their controlling object ends, in reverse order of acquisition.

Надо побольше поизучать вопрос

в с++ ничто не мешает вам вернуть unique_pt/shared_ptr на подконтрольный объект.

Rust это и есть вынесение best practice на уровень языка, без которых компилятор не пропустит код. В этом собственно и плюс языка.

И в этом же — его минус. В C++ подобные вещи делаются на уровне библиотеки и, соотвественно, их можно менять. Когда-то best practice — это был auto_ptr, но потом выяснилось, что он небезопасен и появились unique_pte и shared_ptr. А ещё через 10 лет — что-то ещё может появиться.

А в rust — жёстко заложены сегодняшние best practice — что с одной стороны значит что пользоваться ими удобнее (всё-таки часть языка, не библиотеки), а с другой — значит что поменять их будет гораздо сложнее.

Боюсь оценить эти эффекты и увидеть какой из них более весом мы сможем лет через 10, не раньше…

Согласен, но в этом ведь нет ничего плохого. Почему бы не менять язык раз в 10 лет, когда выясняется, что есть еще более "best practice" и в новом языке они как раз реализованы? Слава богу, мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем, lock на конкретном языке потому что на нем что-то написано остается бичом, но только в достаточно редких случаях кровавых энетрпрайзов 20+летней давности.


Лично я с растом когда знакомился, было сложно, но понятно. Но когда я вижу подобные вещи


(X{}.*&X::foo)(); // ill-formed in C++17, well-formed in C++2a

То мне непонятно, плакать или смеяться… Делать по языку каждый год не вариант. Но пихать все подряд в язык на протяжении полусотни лет и надеяться на стандартную библиотеку имхо вторая крайность, не лучше первой.

Почему бы не менять язык раз в 10 лет, когда выясняется, что есть еще более «best practice» и в новом языке они как раз реализованы?.. мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем

А правки вносить методом переписывания целых модулей? Какую пользу это принесет бизнесу в обозримой перспективе?

Зачем переписывать? Обычно переход происходит постепенно, по модулям, да. От появления нового языка старый не исчезает в вспышке адского пламени, а вполне себе сосуществует.

Зачем переписывать?

Чтобы правку сделать, очевидно.

От появления нового языка старый не исчезает в вспышке адского пламени, а вполне себе сосуществует.

То есть для одного и того же проекта вам нужно не n специалистов, а 2n. То есть одни будут поддерживать, другие — мигрировать, третьи — тестировать совместимость… Зачем? Да прост есть один прикольный язык, на хабре писали, что он — убийца C++...
А потом вдруг конкуренты выкатят продукт, который, благодаря тому, что написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку.
А потом вдруг конкуренты выкатят продукт, который, благодаря тому, что написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку.
Примеры — в студию. Я знаю случаи, когда новый продукт замещал старый из-за того, что он был по-другому устроен, но ни разу, никогда и ни при каких условиях не видел, чтобы кто-то выиграл только за счёт того, что новый продукт «написан с использованием современных языков и методологий, будет требовать меньше ресурсов на написание и поддержку».

В большинстве случаев использование другого языка в успехе продукта — дело десятое. За исключением случаев когда старый продукт по тем или иным причинам становится недоступен (как с J++, скажем, произошло или с Visual Basic'ом).
Что если мы бросим ресурсы на никому ненужный хипстерский язык, а конкуренты во всю будут пилить киллер-фичи? Мне кажется, что этот «вдруг» вероятнее.
Это хороший пример. Только «читать» нужно не то, что там написано, а то, о чём там не написано.

Ибо после того, как возможности переросли масштабы, которые смогла поддерживать небольшая первоначальная команда, Yahoo Store потерял лидерство (Shopify, eBay и даже Amazon сейчас имеют больший оборот) и тот факт, что «в Yahoo на серверах работает софт, написанный на всех пяти языках, которые советует хакерам Эрик Реймонд» их не спас, как ни удивительно.

Так что непохоже, что подход Самыми безопасными были те, кому требовались специалисты по Oracle. О таких не стоило беспокоиться. Также мы были спокойны, если требовались разработчики на C++ или Java. сработал.

На короткой дистанции — да, «хипстерские языки» (вернее, на самом деле, уникальные разработкичи хорошо интегированные с «хипстерскими языками») могут показать неплохую отдачу, но на длинной «специалисты по Oracle и разработчики на C++ или Java» берут верх. О чём, собственно, и речь.

То есть если вы пишете код, который взаимодействует с базой, то вы нанимаете специалиста по SQL? Мне казалось, это базовый навык для бекенд-разработчика. Точно так же никто не мешает знать параллельно C++ и Rust, тем более, что после пятого языка каждый последующий можно изучить ну буквально за неделю. Остается еще стандартная библиотека, но она как правило осваивается методом гугления.

То есть если вы пишете код, который взаимодействует с базой, то вы нанимаете специалиста по SQL?

В нормальных местах, кстати, бывает специальный дядька, который проектирует базу, пишет хранимки и т.п. (DBA называется), т.е. разработчик бизнес-логики может знать SQL так же, как и фронтовую часть — в общих чертах. Но так как бизнес повально экономит, и бизнес-логика редко бывает сложной в типовых проектах, то появились сердитые «фуллстеки». Стоит ли говорить, что у миддла-фуллстека оклад в среднем выше, чем у такого же узкого специалиста, а качество работы хуже в каждой отдельно взятой дисциплине?
после пятого языка каждый последующий можно изучить ну буквально за неделю

Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=
Читывал я и код джависта на C++, так и C# с венгеркой от сишника. Нафиг таких SO-умельцев, скажу я вам (= Прогресс подарил нам разделение труда, но некоторые программисты почему-то считают себя выше этой ерунды.
В нормальных местах, кстати, бывает специальный дядька, который проектирует базу, пишет хранимки и т.п. (DBA называется), т.е. разработчик бизнес-логики может знать SQL так же, как и фронтовую часть — в общих чертах. Но так как бизнес повально экономит, и бизнес-логика редко бывает сложной в типовых проектах, то появились сердитые «фуллстеки». Стоит ли говорить, что у миддла-фуллстека оклад в среднем выше, чем у такого же узкого специалиста, а качество работы хуже в каждой отдельно взятой дисциплине?

Во-первых как правильно сказано, для большинства проектов отдельный DBA это просто оверкилл. Все же нужно исходить из реальных потребностей, а не "у меня 1000 серверов, поэтому сайт-визитка должен иметь такой же 30-этапный папйлайн деплоя, как и у меня. Что, у вас нет отдельного бекенд-разработчика для врапперов над DALом? Фу, что за непрофессинальность". Я раньше как раз фуллстечил, от SQL и до css, щас больше осел в беке. Только база все равно основополагающее, и знать надо, никуда не деться.


Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=
Читывал я и код джависта на C++, так и C# с венгеркой от сишника. Нафиг таких SO-умельцев, скажу я вам (= Прогресс подарил нам разделение труда, но некоторые программисты почему-то считают себя выше этой ерунды.

Нет, я только за разделение. Только ваша позиция человека, который привык к молотку, и вместо того, чтобы научиться пользоваться еще и отверткой всеми силами пытается доработать молоток, чтоб он еще и завинчивать умел. Я-то как раз за разделение, в данном случае — инструментов. А то получается, что вместо отдельного SQL для баз и отдельного языка общего назначения встроили бы язык запросов прямо в плюсы и зажили бы. Хотя в шарпе так и сделали, и работает весьма неплохо… Мда, плохой пример привел. Но все равно мысли примерно в таком духе.


Кстати, что касается цитаты:


Посмотрел бы я на того героя, который за неделю будет изучать C/C++ впервые! (=

Это ведь скорее минус языка, а не плюс. Тот же шарп или го за неделю изучить можно легко. И это как раз следствие бесконечного расширения. Проблема существующих языках в решениях дизайна, которые были приняты давным-давно, себя не оправдали, но их тянут из соображений обратной совместимости. Тот же раст по-сути это те же плюсы с небольшим сахаром, нормальным пакетным менеджером, и выкинутыми костылями. Ну буквально все улучшили, что не удивительно, за 50 лет хоть 1 язык должен был такого достичь. Но все равно "не тру" почему-то по мнению людей.

Тот же шарп или го за неделю изучить можно легко.
Нельзя их изучить за неделю. Чтобы сделать простенькую утилиту — может быть. А чтобы начать писать идеоматичный код, а не, как в пресловутой притче, «FORTRAN на любом языке» — требуется время сравнимое с изучением C++ со всеми его наворотами.

Но все равно «не тру» почему-то по мнению людей.
Потому что это — другой язык с совсем другой экосистемой. Неясно — сможет ли он вообще набрать критическую массу и ещё менее ясно — будут ли на него переходить с C/C++…
Нельзя их изучить за неделю

Я считаю иначе.


Потому что это — другой язык с совсем другой экосистемой. Неясно — сможет ли он вообще набрать критическую массу и ещё менее ясно — будут ли на него переходить с C/C++…

С99 От С++17 отличается сильнее, чем С++ от раста. И ничего, переходят люди как-то.

НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь

Хаскель не императивный, поэтому как замена императивного С/С++ плохо годится, и у него самого хватает проблем и без этого.
image

НЛО прилетело и опубликовало эту надпись здесь
SQL — это DSL, который заменить не так-то просто. А тредик начинался про смену шила на мыло.
Это ведь скорее минус языка, а не плюс. Тот же шарп или го за неделю изучить можно легко. И это как раз следствие бесконечного расширения.
Не только в расширении дело. Уровень языков разный. C++ — это, по сути, поумневший компилятор C. При этом C синтаксически достаточно простой язык, но при этом писать на нем относительно сложно. А тот же C# уже так разнесло, что спека на него по размеру стремительно приближается с C++, плодя неиспользуемое легаси и просто сомнительные фичи как не в себя.

Использую все фичи C# 7.0, кроме разве что ref locals/ref returns. Можно пример фич, которые никому не нужны? Просто по моему опыту наоборот, есть 100500 миллионов issues на гитхабе, которые просят реальные люди, и их делают не от скуки, а потому что они дают вполне конкретную ценность. И команде приходится выбирать, что делать, а что нет. А не то, что пихают все подряд чисто чтобы хоть чем-то заняться.

Можно пример фич, которые никому не нужны?

Навскидку: устаревший синтаксис создания объектов делегатов, сам класс Delegate, необобщенные коллекции, кортежи, out без предварительного объявления, импорт статических классов.

есть 100500 миллионов issues на гитхабе, которые просят реальные люди, и их делают не от скуки, а потому что они дают вполне конкретную ценность.

Именно поэтому большую часть из этого треша отклоняют?) Да, им это безумно нужно, но не факт, что остальные были бы рады читать такой код. Посмотрите на Java — это, конечно, другая неадекватная крайность, но и в таком консерватизме есть некоторый смысл.
Навскидку: устаревший синтаксис создания объектов делегатов

"Устаревший" синтаксис делегатов полезен, когда не надо перечислять все аргументы функции. Типичный пример:


ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };

сам класс Delegate

А что с ним не так?


необобщенные коллекции

При чем тут язык?


кортежи, out без предварительного объявления

Крайне полезные фичи. Таплы просто кучу мест заменили, где есть приватный класс из двух полей, потому что нам нужно вернуть 2 значения. out без объявления как бы тоже логично, потому что единственная задача out-а это заполнить значение пустой переменной. Очень бесило, что нельзя просто написать while (!tryparse(out x) && x > 3) однострочником, просто потому что на предыдущей строке нужно объявление переменной.


импорт статических классов

Ну видимо вы никогда не писали код, который активно использует фабрики или математику. Пример из моего репозитория: попробуйте убрать using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; и посмотрите, насколько классным и читаемым стал код.


Именно поэтому большую часть из этого треша отклоняют?) Да, им это безумно нужно, но не факт, что остальные были бы рады читать такой код. Посмотрите на Java — это, конечно, другая неадекватная крайность, но и в таком консерватизме есть некоторый смысл.

Из всех фич, что предложили, я считаю бредом только ref locals, хотя бы потому что GetNumber(5) = 10 выглядит очень странно. Но 1 фича из десятков, которые очень нужны и полезны выглядит не очень большой ценой, тем более что она меня никак не задевает.

Типичный пример

Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?
При чем тут язык?

По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.

Крайне полезные фичи.Таплы просто кучу мест заменили, где есть приватный класс из двух полей, потому что нам нужно вернуть 2 значения.

Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.
out без объявления как бы тоже логично, потому что единственная задача out-а это заполнить значение пустой переменной

Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.

Ну видимо вы никогда не писали код, который активно использует фабрики или математику. Пример из моего репозитория:...
using Sf = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;


Но 1 фича из десятков, которые очень нужны и полезны выглядит не очень большой ценой, тем более что она меня никак не задевает.

«100500 миллионов» превращаются в десятки и вы признаете, что мусор плавно просачивается в язык.
Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?

Вы привязываетесь к частностям.


По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.

Во-первых, это не синтаксис, они совершенно никому не мешают.
А во-вторых, предлагаете отказаться от обратной совместимости?


Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.

И не надо их передавать наружу. Используйте их локально там, где это удобно, а где надо выдавать наружу — заведите отдельный класс.


Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.

Почему же исключительный случай? Сделал ради интереса поиск в своём коде по TryGetValue и TryParse — абсолютно во всех случаях строкой выше было объявление переменной, которое можно перенести внутрь вызова функции, причём в виде out var x, без указания конкретного типа, а это экономия до 50 символов!

Вы привязываетесь к частностям.

Без такой частности пример теряет смысл. Если параметры можно игнорировать, то зачем они нужны? Примерно так же в пользу goto можно приводить 4-5 вложенных цикла и говорить, что это решает все наши проблемы.

Во-первых, это не синтаксис, они совершенно никому не мешают.
А во-вторых, предлагаете отказаться от обратной совместимости?

Это был один из примеров накопления легаси в отдельно взятом C#.

И не надо их передавать наружу. Используйте их локально там, где это удобно.

Чтобы не передавать данные наружу уже есть анонимные типы.

Почему же исключительный случай?

Очевидно, других подобных конструкций грамматика языка не допускает.
Без такой частности пример теряет смысл. Если параметры можно игнорировать, то зачем они нужны?

А функциям, в определении которых присутствуют параметры по умолчанию, вы так же плохо относитесь?


Примерно так же в пользу goto можно приводить 4-5 вложенных цикла и говорить, что это решает все наши проблемы.

А ещё есть люди, которые утверждают, что использование return эквивалентно goto, и что return должен быть только один — в конце функции.


Это был один из примеров накопления легаси в отдельно взятом C#.

Да, это плохо.
Ещё ещё один плохой пример легаси, который раздражает меня гораздо больше — типы-перечисления (enum) не реализовывают IEquatable<T>. Казалось бы, добавить интерфейс — не проблема, но это может поломать старый код.


Чтобы не передавать данные наружу уже есть анонимные типы.

Но вы не можете использовать их в объявлении приватной функции, а вот кортежи — можете.


Очевидно, других подобных конструкций грамматика языка не допускает.

Вы можете использовать out параметры где угодно. Просто есть типичный usecase для них.

Сомнительный пример, если честно. Зачем нужна такая затычка для валидации?

Это пример кода, а не дизайна.


По странному стечению обстоятельств спека языка включает в себя API стандартной библиотеки.

Не поленился, залез в спеку, нашел там только упоминания IEnumerable<T> (потому что компилятор специально обрабатывает yield) и IList<T> (потому что компилятор должен уметь преобразовывать в него такой базовый тип, как массив). Всё. Больше никаких упоминаний, тем более не-дженериков, кроме как в примерах нет.


Это дефакто неименованные типы, которые нужно передавать во внешний скоуп — зло для читателя без очевидной пользы.

Приватный метод, который должен вернуть пару значений. Вот опять же пример из существующей кодовой базы:


private IEnumerable<(string AgName, string PrimaryServer, string SecondaryServer)> GetAvailabilityGroupsOn(string serverName)
{
    var connectionString = GetConnectionString(serverName);
    using (var connection = new SqlConnection(connectionString))
    {
        connection.Open();
        var agQuery = new SqlCommand(AgQueryText, connection);
        using (var reader = agQuery.ExecuteReader())
        {
            while (reader.Read())
            {
                yield return (reader.GetString(0), reader.GetString(1), reader.GetString(2));
            }
        }
    }
} 

Метод этот используется один-единственный раз в LINQ-запросе


var availabilityGroups = databaseServers.SelectMany(GetAvailabilityGroupsOn).Distinct().ToList();

Что, ради этого класс городить? Или обычный тапл использовать и потом с Item1/Item2 возиться?


Исключительный случай в языке, когда переменная объявляется внутри вызова функции, сомнительная польза.

Так единственный случай, когда out используется по назначению это TryXXX операции. Не считая интеропа.


using Sf = Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

Ну так замените. Что, стало читабельнее? Понятнее? Как по мне, так нет. Просто бесполезный суффикс, "чтобы было как всегда было".


«100500 миллионов» превращаются в десятки и вы признаете, что мусор плавно просачивается в язык.

У меня диаметрально противоположенный вывод, из 100500 миллионов просачиваются единичные золотые предложения.

Не поленился, залез в спеку, нашел там только упоминания IEnumerable<T>… Всё. Больше никаких упоминаний, тем более не-дженериков, кроме как в примерах нет.

Да, согласен, почему-то я считал иначе (как это у C++ заведено).

Приватный метод, который должен вернуть пару значений. Вот опять же пример из существующей кодовой базы:

Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.

Ну так замените. Что, стало читабельнее? Понятнее? Как по мне, так нет. Просто бесполезный суффикс, «чтобы было как всегда было».

Это заметно снижает уровень драмы) Под соусом новизны подаётся то, что в том же JavaScript считается плохим дизайном и старательно изгоняется из языка.
Лучше бы разрешили свободные функции и вопроса бы не стояло, а так статический импорт — это просто костылик.
Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.

Предлагаете yield return (reader.GetString(0), reader.GetString(1), reader.GetString(2)); заменить на yield return ProcessResult(reader), где ProcessResult — передаваемая параметром лямбда, возвращающая анонимный тип?


Да, такое, в принципе, возможно. Но нужно смотреть на остальную архитектуру кода.

Ну вот опять. Это явно типичный код, который напрашивается на дженерелизацию (да и отделение DAL), тогда заведение отдельного типа было бы вполне логично.

Он не имеет смысла в другом контексте. Вот у вас есть код, который использует анонимные классы? Наверняка есть. Теперь предположим, что он используется в одном методе в трех разных местах. Что хочется сделать? Правильно, вынести функцию. И вот тут получается, что нельзя просто так вынести функцию, которая возвращает анонимный тип. А кортеж — можно. В итоге, как только хотим вернуть более 1 значения из функции начинаем засорять глобальное пространство имен ничего не значащими DbScanResult, StorePaymentPair и прочими, вся суть которых просто в том, чтобы передать 2 значения.


Это заметно снижает уровень драмы) Под соусом новизны подаётся то, что в том же JavaScript считается плохим дизайном и старательно изгоняется из языка.
Лучше бы разрешили свободные функции и вопроса бы не стояло, а так статический импорт — это просто костылик.

Это добавляет информационный шум безо всякой ценности. Там около 200 использований функций этого класса в коде, добавили 3 символа, итого просто +600 символов на ровном месте. Кому они были нужны? Зачем? Что хорошего они добавили? Непонятно.

Он не имеет смысла в другом контексте. Вот у вас есть код, который использует анонимные классы? Наверняка есть. Теперь предположим, что он используется в одном методе в трех разных местах. Что хочется сделать?

Если что-то используется больше одного раза (не создание-вызов-получение, а действительно похожие использования), то я бы вопроса такого не ставил — отдельный тип. Чтобы был явный контракт, с отслеживаемыми юзаджами и прочим.

Это добавляет информационный шум безо всякой ценности.

Вы считаете, что набор полей без общего имени информационный шум не увеличивает?) Есть же регионы для группировки

Там около 200 использований функций этого класса в коде, добавили 3 символа, итого просто +600 символов на ровном месте.

У меня есть такое подозрение, что объявление типа кортежа почти всегда будет длиннее, чем имя класса
Если что-то используется больше одного раза (не создание-вызов-получение, а действительно похожие использования), то я бы вопроса такого не ставил — отдельный тип. Чтобы был явный контракт, с отслеживаемыми юзаджами и прочим.

Какой тип вы будете создавать для того, чтобы вернуть две айдишки? StoreIdPaymentIdPair? И много он дает выигрыша по сравнению с методом который возвращает (int StoreId, int PaymentId) ?


Вы считаете, что набор полей без общего имени информационный шум не увеличивает?) Есть же регионы для группировки

Набор полей не является самостоятельной сущностью. Это то, чем по определению являются кортежи.


У меня есть такое подозрение, что объявление типа кортежа почти всегда будет длиннее, чем имя класса

Конечно длиннее, в примере выше аж на 8 символов. Ну, правда не надо забывать еще объявление класса строк на 10, но это же такие мелочи.


Подводя итог (уже немного поднадоело писать одно и то же): таплы это отличный способ вернуть несколько значений из метода. Раньше нужно для этого было либо out-параметры воротить, либо именованный класс делать. И плодились просто десятки классов из 2-3 полей, которые используются в одном месте. Если метод публичный — не вопрос, именованный класс должен быть всегда (другой вопрос, зачем публичный метод должен вернуть айдишки, а не объект модели. Так что такой вопрос на практике обычно не стоит). А вот если они приватный, то он абсолютно ничем не хуже анонимного типа. Анонимный тип имеет скоуп метода, таплы — класса.

Какой тип вы будете создавать для того, чтобы вернуть две айдишки? StoreIdPaymentIdPair?

Тип описывается в том числе свойствами, поэтому что-то вроде StorePayment { StoreId; PaymentId }

Набор полей не является самостоятельной сущностью.

Как это? Вы же группируете данные по какому-то признаку. Даже в вашем примере было GetAvailabilityGroupsOn

Конечно длиннее, в примере выше аж на 8 символов. Ну, правда не надо забывать еще объявление класса строк на 10, но это же такие мелочи.

Речь идет о том случае, когда больше одного использования. Но мне не так важны символы, у меня они бесплатные, сколько возможность однозначно установить, что во всех этих местах используется тот же тип, рефакторить, наследовать и прочее.

А вот если они приватный

А если публичный, то в чем отличие?

К сожалению, в C# пока нет механизмов эффективного определения таких классов. Да, можно создать класс struct StoreIdPaymentIdPair { int StoreId; int PaymentId; }, но пользоваться им будет неудобно, потому что вместо return (a, b); придётся писать return new StoreIdPaymentIdPair { StoreId = a, PaymentId = b };.


Мало определить только поля класса, нужно ещё создать конструктор, деконструктор, переопределить Equals, GetHashCode, ToString. Всё то, что в туплах уже есть. Вы хотите вручную создавать такой класс? Лично я — нет.


Фактически, всё, что нужно — это именованное определение туплов, что-то типа using StoreIdPaymentIdPair = (int StoreId, int PaymentId). И я уверен, что в будущих версиях C# это будет сделано.

И я уверен, что в будущих версиях C# это будет сделано.

Это называется Records, должно было появиться уже в 7 версии, но перенесли.

Видимо, не договорились с синтаксисом, и выпустили урезанный вариант Records в виде синтаксического сахара для ValueTuple.


В любом случае, очевидно, что лучше написать


public struct Pair(object First, object Second);

а пока нет Records — просто использовать ValueTuple в виде (object First, object Second),


чем на каждый чих писать простыню кода:


Код
public struct Pair : IEquatable<Pair>
{
    public object First { get; }
    public object Second { get; }
    public Pair(object First, object Second)
    {
        this.First = First;
        this.Second = Second;
    }
    public bool Equals(Pair other) // for IEquatable<Pair>
    {
        return Equals(First, other.First) && Equals(Second, other.Second);
    }
    public override bool Equals(object other)
    {
        return (other as Pair)?.Equals(this) == true;
    }
    public override int GetHashCode()
    {
        return (First?.GetHashCode()*17 + Second?.GetHashCode()).GetValueOrDefault();
    }
    public Pair With(object First = this.First, object Second = this.Second) => new Pair(First, Second);
    public static void operator is(Pair self, out object First, out object Second)
    {
        First = self.First;
        Second = self.Second;
    }
}
А если публичный, то в чем отличие?

В том же, почему приватные поля это ок, а публичные — не очень.


Как это? Вы же группируете данные по какому-то признаку. Даже в вашем примере было GetAvailabilityGroupsOn

Ну необязательно. Другой пример — словарь с ключами по 2 значениям:


private readonly ConcurrentDictionary<(int actionTemplateId, int apiProjectId), EmailMailingProperties> 
            emailMailingPropertiesCache = new ConcurrentDictionary<(int, int), EmailMailingProperties>();

...

var result = emailMailingPropertiesCache[(actionTemplateId, projectId)];

Зачем тут городить явный класс? Тем более что да, нужно реализовывать IEquitable и все прочие прелести.


Заметьте, что во всех примерах все использование ограничено пределами класса и приватными переменными. Все потому, что отдавать наружу ValueTuple<TItem1, TItem2> действительно некрасиво. Но для скоупа класса это более чем допустимо.

НЛО прилетело и опубликовало эту надпись здесь

Насколько я понимаю, у этого подхода есть проблема с reference assemblies. Суть в том, что в шарпе зачастую референсятся только декларации, а про тело метода ничего неизвестно. То есть грубо говоря у нас есть хедер-файлы, но мы ничего не знаем про содержимое. В таком случае у нас есть


auto doFoo() => throw null;

Пример из стандартной библиотеки. И как вывести отсюда тип — непонятно. И второе, в шарпе вывод типов работает от декларации, то есть нельзя написать.


var x;
x = 10;

И вывести тип int. Поэтому в плюсах это работает, а в шарпе — нет.

НЛО прилетело и опубликовало эту надпись здесь
Использую все фичи C# 7.0, кроме разве что ref locals/ref returns

Последние я тоже начал использовать. Usecase: передача функции, которая возвращает ссылку на поле объекта. До C# 7.0 приходилось передавать две функции (геттер и сеттер).

Обычно переход происходит постепенно, по модулям, да.
«По модулям» он происходиттогда, когда у вас нет выбора. Вот посмотрите сюда — где вы тут видите «переход по модулям»? Просто фичи из новых версий языка постепенно разрешаются и людим потихоньку переучиваются. Без руволюций и тотального переписывания всего с нуля.
А зачем что-то менять или переписывать? Нужно либо сделать так, чтобы компилятор понимал несколько языков, либо транслировать их в некоторое универсальное представление.

Хорошим примером здесь будет .NET и JVM: вы можете писать модули на разных языках, и они будут совместимы друг с другом.

Бич C++ — отсутствие такого универсального представления. Так, интерфейс модуля распространяется не в виде метаданных, а в виде исходных файлов, что привязывает к конкретному языку.
Хорошим примером здесь будет .NET и JVM: вы можете писать модули на разных языках, и они будут совместимы друг с другом.

Однако, вы часто встречали в продакшне проекты под .NET не на C#? Не Android, где цикл поддержки приложения в несколько лет, а махровый интерпрайз. Это идея не взлетела, ибо чтобы поддерживать мультиязычные приложения нужно иметь мультиязычных специалистов или несколько специализированных команд.

И, да, касамо .NET, это выливается в такое уродство, как CLS, на которое ни один здравый архитектор равняться не станет. А история с унылыми дженериками Java также достаточно известна — груз совместимости JVM. Чудес не бывает.

Миллион раз видел математическую часть написанную на F#. Иногда видел части на managed C++. На других — не особо, но и этого уже достаточно.

Я поддерживал проект как с C++/CLI, так и C++ в связке с C#. Докладываю: адок еще тот (= Но это было необходимым злом — железки. В здравом уме языки мешать — так себе затея.
F# лично не встречал еще. И что, серьезные крупные проекты так пишут?

Вполне. Насчет плюсов — согласен, я их до кучи привел :) А вот F# вполне себе, на том же дотнексте много докладов на его тему, а голым его почти никогда не используют. Учитывая совместимость на уровне CIL вполне себе отлично сосуществуют.

на том же дотнексте много докладов на его тему
Ну это так себе показатель, если честно (=

Это просто как пример областей применения. Так-то я и локально все это видел. Но ссылку на это дать затруднительно :)

Под .NET есть еще такой язык как Poweshell. И еще можно многие JVM-либы использовать через IKVM.

При желании можно прикрутить что угодно к чему угодно. Дело в цене такой операции и поддержки после.

Писал в свое время целую систему под ЕМИАС на powershell. Идея была в том, что если багу найдет админ на проде, он сразу сможет на месте поправить её и таки образом сокращается время обратной связи. В принципе, часто работало, хотя часто знания админов не дотягивали до того, чтобы понять ошибку в логике скрипта.

Слава богу, мы сейчас можем спокойно переиспользовать написанные на одном языке компоненты в другом безо всяких проблем
Компоненты мы использовать можем, программистов нет. Что бывает, когда языки пытаются менять каждые 10 лет на несовместимые с ними отлично показывает цепочка: Алгол — Паскаль — Модула-2 — Оберон: от языка, вокруг которого, фактически, целая индустрия построена была к просто «популярному» языку, далее к «перспективному» и, наконец, в забвение.

Проблема в том, что каждый раз, когда вы меняете язык несовместимым образом у людей появляется соблазн перейти не на новую версию, а что что-то совсем другое. Скажем Андроид переходит не с Python2 на Python3, а с Python'а на Go…
Проблема в том, что каждый раз, когда вы меняете язык несовместимым образом у людей появляется соблазн перейти не на новую версию, а что что-то совсем другое. Скажем Андроид переходит не с Python2 на Python3, а с Python'а на Go…

Мне кажется, что саботировать переход на другой стек это несколько отдает сектантством. Если переходят с питона2 на го, значит го лучше питона3 по каким-то причинам. Что плохого в переходе на го я не вижу, если честно.

Что плохого в переходе на го я не вижу, если честно.
Плохо то, что приходится менять всю инфраструктуру, все инструменты, переписывать (постепенно) все программы, переучивать людей под новую парадигму и прочее, прочее прочее. Но если у вас язык меняется несовместимым образом — то, собственно, выбора у вас и нет: выбирать «идейного потомка» языка, на котором всё написано большого смысла не имеет.

Мне кажется, что саботировать переход на другой стек это несколько отдает сектантством.
Кто говорит о саботаже? Если бы python2 продолжал развиваться и никто не заставлял бы разработчиков никуда переходить, то он так бы и использовался. Постепенно какие-то фичи бы внедрились, какие-то — «остались бы за кадром». И это было бы всяко лучше «революции» с переходом на python3 или go. Собственно так переход на C++14 произошел.
Плохо то, что приходится менять всю инфраструктуру, все инструменты, переписывать (постепенно) все программы, переучивать людей под новую парадигму и прочее, прочее прочее. Но если у вас язык меняется несовместимым образом — то, собственно, выбора у вас и нет: выбирать «идейного потомка» языка, на котором всё написано большого смысла не имеет.

Ну это стоимость перехода. Никто не мешает оставаться на существующем тулчейне и получать эволюционные изменения.


Кто говорит о саботаже? Если бы python2 продолжал развиваться и никто не заставлял бы разработчиков никуда переходить, то он так бы и использовался. Постепенно какие-то фичи бы внедрились, какие-то — «остались бы за кадром». И это было бы всяко лучше «революции» с переходом на python3 или go. Собственно так переход на C++14 произошел.

Так питон2 и продолжает развиваться, разве нет?

Ну это стоимость перехода.
Стоимость мы оплатим реальными деньгами сегодня, а польза-то есть? В обозримом будущем, а не через 10 лет.

Ну это же не я должен считать, а техлид вашего проекта. Посмотреть фичи, посчитать, сколько экономит, посчитать затраты, принять решение. Обычный процесс, ничем не отличается от, например, обычного рефакторинга архитектуры.

Обычный процесс, ничем не отличается от, например, обычного рефакторинга архитектуры.
Отличается. В случае «с обычным рефакторингом» масштаб бедствия ограничен тем компонентом, который рефакторится. В случае с переходом на новый язык — вам приходится переписывать всё. Что не имеет смысла почти никогда.

Варинат: оставить в проекте в долгосрочной перспективе два языка обычно хуже любого из них, так как вам после этого потребуются специалисты знающие их оба. Что, как правило, неприемлемо.

P.S. В большинстве проектов идёт работа с двумя, тремя, а то и с десятью языками — но в разных нишах: никому в голову не придёт идея переписать какой-нибудь компонент с SQL на CSS, к примеру. А тут, раз уж мы берёмся переписывать, то ясно, что это языки «из одной ниши». В этом случае переход может быть осмысленным в том случае если «новый» язык является «продолжением» старого — как в случае с переходом с C на C++ или с C++98 на C++11. Если же этого нет — то, как правило, смысла переходить тоже нет.
Ну мы же спорим с точки зрения техлида. Разработчикам-то понятно: мы любим крутые новинки и рады внедрению новых языков/стандартов) ведь при плохом раскладе мы всегда можем сменить работу Теоретически, при прочих равных, я с вами согласен — менять языки на более эффективные и удобные нужно.
Так питон2 и продолжает развиваться, разве нет?
Если вы про Tauthon, то это не совсем то: неофициальная сборка, очень мало пользователей. Хотя чем бог не шутит, может и взлетит. А так — официальная поддержка python2 жёстко прекращена, всех пинками пытаются загнать в рай.

Это дало очень серьёзный толчок развитию Go, так как внушительный процент питонистов просто ушёл на Go, Android — не единственная история.

Андроид переходит с Python'а на Go? А можно поподробнее?

Андроид переходит с Python'а на Go?
Пока нет. Переход на Go — рассматривается как один из вариантов. Поддержка своего форка python'а — как другой. Переход на Python3 не рассматривается.

Пока на Go пишутся вспомогательные игрушки типа kati и soongа. По результатам будет видно — будет переход на Go или нет. Причём если kati — это, как описано, просто клон make (на самом деле не совсем), то soong — это уже использование Go как скриптового языка…
Ну насколько я понимаю, RAII говорит про инициализацию ресурса, а не про его дальнейшее использование.
Давайте спорить о вкусе устриц с теми, кто их ел?

Читаем
Получение ресурса есть инициализация (англ. Resource Acquisition Is Initialization (RAII)) — программная идиома объектно-ориентированного программирования, смысл которой заключается в том, что с помощью тех или иных программных механизмов получение некоторого ресурса неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта.
Уж одно-то предложение можно было прочитать перед тем, как спорить до хрипоты о том, чего вы не знаете?

RAII — это как раз только, исключительно и обязательно «о дальнейшем использовании» — иначе какой бы в нём был смысл?

Про момент его уничтожения и очистки тут ничего не сказано.
Вы это серьёзно? Получение некоторого ресурса неразрывно совмещается с инициализацией, а освобождение — с уничтожением объекта — это, как раз, об очистке.

Собственно вся идиома придумана только и исключительно для того, чтобы правильно удалять обьекты, а не создавать их!

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

Сборка мусора и RAII это ортогональные вещи. RAII намного более универсальная вещь. Сборка мусора решает только одну проблему — освобождение памяти. Но остается еще одна — захват и освобождение ресурсов. Так вот, вторую проблему сборка мусора не решает, так как вы не можете детерминировано (автоматически, не «руками») освободит ресурс пользуясь только ею.
Не вместо, а вместе.
Структуры собираются при выходе из области видимости, ссылочные — сборщиком.
Ссылочные типы могут быть завёрнуты в структуру и удалены детерминированно.

Это мало меняет дело, коль уж стандартная библиотека рассчитана на сборку мусора.

C#, Java, Objective D, Go, Rust, Swift — это попытка сделать замену С/С++

И кто из них самый быстрый?

Ну уж точно не C# и не Java. Но кому это сейчас важно?

Самый быстрый тот у которого нативный кодогенератор и развитые средства метапрограмирования. Но вот C# например самый красивый синтаксически. В Go, Rust, Swift есть некоторые идеи, которые при синтаксисе C# были бы вообще конфетки. В D весьма качественно проведена «работа над ошибками» С++, учтены и продуманы очень многие мелочи (это чрезвычайно важно, дьявол как известно именно в мелочах), но к сожалению там больше влияния Java чем остальных языков из списка.
Угу, синатксис C# очень приятный и лаконичный. Например, лямбда для возведения в квадрат в C# выглядит как `x => x * x`, тогда как в C++: `[](auto x) { return x * x; }`.

Просто C++ дорабатывается инкрементально, допиливанием нового функционала поверх старого, причём так, чтобы ничего из старого не поломалось, вот и получается многословность и куча неудобств.

В C++ же мне очень не хватает двух вещей, которые есть в C#: concepts и extension method.
Первое приходится имитировать через CRTP, что немного неудобно, второе — через объекты-адаптеры (пример).
И зачем в C++ extension method нужны? Это просто синтаксический сахар, который для консистентности в чисто ОО языках. C++ мультипарадигменный и имеет свободные функции
Вы в своём же комментарии ответили на свой вопрос. Это просто удобный синтаксический сахар. Почему можно где угодно перегружать операторы, а «добавлять» к классу методы — нет?

В C++ можно обойтись без auto, можно обойтись без лямбд, явно реализуя классы-функторы и т.д., но с сахаром-то лучше.
Наверное, потому что это делает ту же работу, что и переопределение свободных функций (см стандартные std::swap, std;:begin, std::end)? Зачем делать дублирующиеся возможности в языке? Просто что бы было «как в C#'?
> Зачем делать дублирующиеся возможности в языке?

Затем же, зачем в С++ дублируются и другие возможности, о которых я писал выше. Вызов конструктора с круглыми или фигурными скобками, auto и decltype, лямбды и объекты-функторы — это всё сахар. Единственное нововведение после С++03, не являющееся сахаром — это move semantics.

Я просто хочу писать `v.sort()` вместо `std::sort(v.begin(), v.end())`.

Тогда уж v.std::sort() получится. И я не вижу чем это лучше гипотетического std::sort(v)

ну когда один аргумент, то вроде без разницы. Но, как на меня, первое смотрится хуже чем второе:

if between(foo, min, max) {
 // do something
}

if foo.between(min, max) {
 // do something
}
НЛО прилетело и опубликовало эту надпись здесь
Вы, видимо, не особо связывались с C#, раз не очень меня понимаете.
Fluent interface для любой коллекции (класса с интерфейсом `IEnumerable`) — чертовски удобная вещь.

Если вам надо сделать несколько действий над коллекцией, например выборку по условию, затем преобразование, затем выбор первого результата, то в C# будет выглядеть так:

var x = v.Where(...).Select(...).First();


Ничего лишнего, просто и понятно.

В C++ из коробки же такого функционала нет. Сначала вызывать `copy_if`, создавая новую коллекцию, затем `transform`, снова создавая коллекцию. Либо, вручную писать цикл `for` по элементам коллекции, что не очень приятно.

Либо вообще писать так:
first(select(filter(v, ...), ...), ...)


Видимо, я действительно очень хочу uniform call syntax.

В C# расширения используются во многом потому что у интерфейсов не было методов с реализацией. Но в С++ нет интерфейсов...

Не совсем понятно, что происходит с памятью в красивой строчке типа var x = v.Where(...).Select(...).First();. Создаётся (а потом уничтожается) несколько временных объектов? Как написать такое же на C++, что будет у такой реализации под капотом?

Не совсем понятно, что происходит с памятью в красивой строчке типа var x = v.Where(...).Select(...).First();

Используются ленивые вычисления, временные коллекции не создаются, за редким исключение


Как написать такое же на C++, что будет у такой реализации под капотом?

Пример раз, пример два.


Мне, правда, ни то, ни другое не нравится. Это же C++, почему бы не использовать статический полиморфизм?

LINQ может работать не только с памятью, но и с БД, В последнем случае количество созданных объектов будет ровно 1 — тот, который вернет метод First. Остальное сконвертируется в запрос к БД.


Если мы итерируемся в памяти, то каждый вызов будет создавать один легковесный объект итератора, который по сути имеет одну ссылку на перечисляемую коллекцию (или предыдущий итератор), и умеет его как-то преобразовывать. В итоге количество созданных объектов — 1 + количество легковесных итераторов. Но на них обычно всем пофиг, тем более, что это обычно структуры, которые еще и автоматически разрушаются при выходе из скоупа.


Ну и напоследок, я все мечтаю написать свой препроцессор, который все это дело будет разворачивать. В принципе, такой уже есть, как stand-alone обертка над компилятором, но хотелось бы что-то встроенное в IDE.

LINQ может работать не только с памятью, но и с БД

Есть маленький нюанс: в этом случае лямбды, которые передаются в качестве параметров методам Select, Where, не компилируются в код, а представляются в виде Expression tree, т.е. нужна поддержка со стороны компилятора. Без этой поддержки Expression Tree пришлось бы строить вручную, и это было бы менее удобно, чем написание SQL-запросов руками.


Ну и напоследок, я все мечтаю написать свой препроцессор, который все это дело будет разворачивать. В принципе, такой уже есть, как stand-alone обертка над компилятором, но хотелось бы что-то встроенное в IDE

А с какой целью? Ради увеличения производительности? А вы уверены, что производительность упирается именно в LINQ-запросы?


В C++ этого эффекта добиться легко (статический полиморфизм), в C# без кодогенерации же не обойтись, т.к. инлайнить лямбды он не умеет.

Есть маленький нюанс: в этом случае лямбды, которые передаются в качестве параметров методам Select, Where, не компилируются в код, а представляются в виде Expression tree, т.е. нужна поддержка со стороны компилятора. Без этой поддержки Expression Tree пришлось бы строить вручную, и это было бы менее удобно, чем написание SQL-запросов руками.

Компилятор тут не при чем. Можно свой провайдер написать, который вместо скирпта SQL генерирует стихи. Компилятор про LINQ ничего не знает, и наоборот, добавление информации о LINQ в компилятор нежелательно:


Currently all of the LINQ keywords do explicitly translate to methods following the LINQ convention. It doesn't even care if they are instance methods or extension methods. This enables designing LINQ capable APIs that aren't enumerable, or even compile-time specialization of LINQ implementations.

In fact, having the compiler explicitly rewrite LINQ keywords could break existing code. I can currently write extension methods to specialize LINQ implementations and the compiler will prefer that implementation over the one provided by the BCL.

The C# compiler knows nothing about where any type/method comes from. And I don't think it should.

Это что касается кейвордов. А методы как были методами, так и остаются. Вопрос, какая такая поддержка компилятора еще нужна LINQ?


А с какой целью? Ради увеличения производительности? А вы уверены, что производительность упирается именно в LINQ-запросы?

Уверен

Компилятор тут не при чем. Можно свой провайдер написать, который вместо скирпта SQL генерирует стихи. Компилятор про LINQ ничего не знает.

Зато компилятор знает про Expression tree, которых в C++ нету.

Компилятор знает про один из видов записи экспрешнов в виде лямбды. Никто не мешает вручную собрать его с помощью фабрики класса Expression. Поддержка со стороны компилятора облегчает запись, но не необходима.

Компилятор знает про один из видов записи экспрешнов в виде лямбды. Никто не мешает вручную собрать его с помощью фабрики класса Expression.

Точно так же никто не заставляет вас использовать LINQ. Вы можете вручную конструировать SQL-запросы. Вы можете вручную разворачивать LINQ-код для повышения производительности.


Поддержка со стороны компилятора облегчает запись, но не необходима.

Неужели вы не понимаете, что без этой поддержки компилятором ни один нормальный человек бы не стал бы тогда использовать LINQ?

Хороший поинт, пожалуй соглашусь.


Правда, уже несколько лет как можно самому сделать такую поддержку как плагин для компилятора (а-ля LLVM мидлварь). Так что с одной стороны я согласен, что поддержка нужна. С другой стороны, практически любой программист может сам её завести, написав за пару дней соответствующий плагин или взяв уже существующий.


Вы можете вручную разворачивать LINQ-код для повышения производительности.

Могу. Но почему-бы этим не заняться компилятору? У него вся информация на руках, остается только ей воспользоваться.

НЛО прилетело и опубликовало эту надпись здесь
Как раз недавно нужна была ORM, но я у меня моделей в базе немного, поэтому не найдя ничего подходящего решил писать sql запросы вручную.
Можно ссылочку на гитхаб?
НЛО прилетело и опубликовало эту надпись здесь
в с++20 вероятно появятся Ranges. Одна из «фич» этого нововведения как раз состоит в том, что все методы, принимающие пару итераторов, смогут принимать один Range-объект. Значит, будут валидны и sort(v);, и vector v = list{1,2,3}; и прочее
п.с. кстати, есть же еще propolsal по uniform call syntax. Чтобы можно было писать std::sort(v) и v.sort(), вызывая по факту один метод

добавлять методы к классу можно если методы шаблонные путем специализаций. Это не идеал, но очень похоже

В D к примеру есть UFCS (Unified Function Call Syntax), который считает эквивалентными код
F(x, a, b, c) и x.F(a, b, c). И есть чёткие правила поиска. Притом работает для любой свободной функции как минимум с одним аргументом.
В C# приходится лапками больше поработать, чтобы сделать такое. C++ половину пути в одну сторону преодолел в виде специализаций шаблонов типа std::swap, а вот в другую сторону путь даже не начинал.
ну как не начинал, драфты уже давно появились:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0131r0.pdf

Были же предложения и Саттера и Страуструпа. Возможно, к C++20 сделают, хотя надежды всё меньше.

И зачем в C++ extension method нужны? Это просто синтаксический сахар, который для консистентности в чисто ОО языках. C++ мультипарадигменный и имеет свободные функции

А зачем тогда С++ нужен, если есть ассемблер? А функции это синтаксический сахар для инструкции CALL.
И зачем в C++ extension method нужны?

чтобы иметь возможность переопределять поведение свободных функций для собственных классов.
Например: у std::list есть метод sort(), однако std::sort для std::list неприменим, т.к. std::list::iterator не удовлетворяет требованиям RandomAccessIterator. С UCS std::sort(list) будет эквивалентен list.sort();
В C# тоже в общем-то. Помнится, был жуткий вой среди разработчиков при введении нового ключевого слова var.
После этого но одно новое средство языка не ломало обратную совместимость в том смысле, что новый компилятор всегда компилировал код, написанный под более старую версию компилятора. Отсюда растут некоторые странные конструкции, такие как pattern matching например.
кому это сейчас важно?

Очень Важно, потому что C++ применяется там, где важна именно скорость. А там где скорость не важна — возможно использовать любой другой язык.
=> язык претендующий на то чтобы заменить собой C++, должен быть достаточно быстрым.

На самом деле не только скорость. На нём ещё микроконтроллеры программируют где память в сотнях байт (даже не в килобайтах!) меряется. Так что из всего этого разнообразия только D и Rust могут на полноценную замену C++ претендовать… И только только в перспективе…
нет у них перспективы, они на слуху из-за одного лишь хайпа. С++ развивается быстрее того же D, а Rust по синтаксису не лучше плюсов
НЛО прилетело и опубликовало эту надпись здесь

Ну я пробовал — как по мне намного лучше. Нормальные дженерики (а не темплейты), приятный синтаксис паттерн матчинга, not-null по дефолту и безопасная работа с памятью. Все это на голову выше просто уровня плюсов.

Плюсы тоже бывают разные. Если следовать Core C++ Guidelines, то жизнь резко становится гораздо проще

А в C++ чем работа с памятью небезопасная? Не используйте new и delete, и будет вам счастье.

А ещё забудьте о reinterpret_cast и постарайтесь обойтись без указателей.

А разве это сложно?

При нормальной архитектуре он и не нужен, а если вы пишите
(Type1)valOfType2
или
 reinterpret_cast<Type1>(valOfType2)
, компилятор считает, что вы знаете, что делаете и не получите в итоге UB.
А обычные указатели не так уж и часто нужны(если не писать на Qt, но там сборка мусора немного своеобразная), для массивов есть контейнер std::vector, для всего остального умные указатели std::unique_ptr, std::shared_ptr, std::weak_ptr, с которыми проблем не возникает.
НЛО прилетело и опубликовало эту надпись здесь
Вы про циклические зависимости? Для решения этих проблем есть std::weak_ptr
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
недавно была статья об умном указателе на с++, который при разыменовании захватывает мьютекс.
НЛО прилетело и опубликовало эту надпись здесь
Нет. Оператор -> оборачивающего указателя переопределен и возвращает прокси-объект, через RAII захватывающий и освобождающий мьютекс. Соответственно, при вызове ptr->someMethod(); мьютекс будет захвачен в течение всего времени выполнения someMethod()
НЛО прилетело и опубликовало эту надпись здесь

Компилятор тоже делает программист. В том же Rust как-то уже находили ошибки в borrow checker-е.

НЛО прилетело и опубликовало эту надпись здесь

Зато гибкости гораздо больше. А все эти unsafe, позволяющие реализовывать подобное в Rust, выглядят как костыль.

НЛО прилетело и опубликовало эту надпись здесь
Но кому это сейчас важно

На фоне приближения к пределу размеров транзисторов — ещё как важно.

Ага
2D Graphics

И зачем это в стандартной либе?
лет 20 назад я бы тут бесновался — «как вы не понимаете?» а сейчас интерфейсы редко пишут на с++ потому что 20 лет назад не было 2Д
Есть у меня подозрение, что тут не в стандартной библиотеке дело. Интерфейсы сейчас частенько пишут на Java, к примеру… совершенно при этом не используя тот 2D, который в стандартной библиотеке Java!
Интерфейсы пишут на специальных фреймворках вроде Qt, которые облегчают их создание или на специальных языках описания интерфейсов типа QML, потому что это удобнее. А в стандартной либе 2D не место, пускай лучше тратят силы на создание того, что нужно большинству.

А на чём тогда пишут интерфейсы всех эти Photoshop, Word, AutoCAD, Chrome и т. д.?

Пора уже переименовать комитет в Boost Standartization Committee, а язык — в Boost++.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации