Обновить
2
2.2

Специалист по теории типов USB-кабелей

Отправить сообщение

Популярность -- это только следствие того, что сделанное в C++ оказалось лучше, чем вы об этом думаете. Шаблоны в C++ оказались практичными, что и стало ключом к их широкому применению.

Вы снова повторяете свой псевдоаргумент о том, что если язык, имеющий нравящуюся вам фичу, популярен, то это говорит о (если вообще не «благодаря») качестве этой фичи.

«Практичные шаблоны C++» (особенно формата и качества реализации а-ля 90-ые) — вы тут на стендап-комика тренируетесь, что ли?

Получилось, что C++ стал первым по настоящему большим полигоном, на котором этот самый параметрический полиморфизм массово испытывался.

Не, маловат полигон. По-настоящему большой полигон — это только питон.

При общении с вами есть еще и такая проблема, что вы никто и звать вас никак.

Ну естественно, это вполне в канве общих отсылок к популярности, авторитетам и прочему подобному.

Я бы скорее удивился, если бы вы что-нибудь такое не сказали.

И что вы можете с этим поделать?

Мне с этим ничего не надо делать. Есть исходное высказывание про полигон, где ничего про популярность нет. Есть вы, влезающий в этот разговор с псевдоаргументом про популярность. Мне достаточно отметить, что тему разговора вы сменили, и всё.

В очередной раз рассказать, что все в Haskell-е все сделано "максимально прямо"?

Нет, конечно. В хаскеле много других вещей сделано довольно криво.

Но, кстати, если вы хаскелисту скажете, что стандартное prelude с partial-функциями кривое, или что без завтипов тяжело (и нормально их в хаскеле не сделают никогда по ряду причин), или тому подобные вещи, то хаскелист не будет вам отвечать «популярность хаскеля в ФП-среде означает, что partial-функции — самые функциональные функции, и что завтипы никому не нужны, хаскельная типизация самая типизированная типизация!»

Если вы сожалеете о впустую потраченной на C++ молодости, то это не значит, что все должны переживать о том, насколько C++ плох и бежать искать что-то лучше.

Я бы, конечно, мог сказать, что автор никому не нужной местечковой библиотеки для акторов конечно будет любить C++ и защищать его всеми силами (включая «популярно ⇒ хорошо»), но не буду.

Вместо этого, раз вам очень хочется обсуждать личности, скажу, что сожалею как раз о том, что потратил некоторую часть этой молодости на высшее образование, математику, и прочее. Без этого всего спокойно программировать на C++ и зарабатывать свои N тыщ в наносекунду — про жопсекурность шаблонов была не шутка — было бы куда проще.

Реальный мир — это когда качество и новизна определяются распространённостью, да (специально для вас — это снова ирония).

Когда плюсовика в очередной раз тыкают в то, что его любимый язык — сборище вековых наслоений из костылей, он в ответ кроет последним универсальным аргументом: «зато популярно!»

То, что было сделано в C++ нашло широкое применение и показало благодаря этому что получилось хорошо

Окей. Открываю TIOBE — там питон на первом месте с трёхкратным отрывом от C++. Практика показывает, что экономия байтиков и микросекунд никому не нужна, и питон лучше, полезнее и качественнее, чем C++. Питон — лучший язык с ведущим подходом к безопасности (проверять типы во время выполнения куда лучше проверок во время компиляции, ведь там больше информации о контексте), наилучшим балансом производительности программиста и результирующего ПО, и самым богатым множеством библиотек. Подход питона к многопоточности показал, что форкать интерпретатор — эффективнее и быстрее всего, а треды в рамках одного процесса никому не нужны.

С++ (особенно современный) по-прежнему никому не нужен, кроме узкого загончика посетителей конференций, которые там занимаются интеллектуальной мастурбацией на никому не нужные монады, обсуждая, зачем их добавили в std::optional , скажем.

Я всё правильно понял?

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

Это в вашем манямирке.

Определение полигона требует массовости применения? Да/нет.

Если бы была востребована только совместимость с Си, то шаблоны в C++ никто бы не использовал.

Что за бредовая логика?

Если нужна совместимость с C, то выбирают язык, где эта совместимость даётся проще всего, не более. Требований на (не)использование других возможностей выбранного языка это не накладывает.

И в рамках C++ эта реализация параметрического полиморфизма полностью и многократно доказала свою состоятельность.

Как предмет для шуток, разве что.

Тут скорее вопрос в том, зачем с ООП работать с такими иерархиями. Я вам пример без ООП привёл, он точно не на ассемблере. Вопрос в том, что вам в нём конкретно не нравится (а не в соломенных чучелах и ложных дихотомиях «без ООП ⇒ на ассемблере»).

Нет. Меня интересуют инструменты, которые широко применяются на практике, которые можно брать и использовать. C++ стал таким инструментом.

Только тут полигоны (о которых шла речь изначально) ни при чём.

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

Востребована и широко использована была совместимость с C, не более.

А то так сейчас дойдём до того, что востребовано и широко использовано UB в каждой второй строчке и remote code execution от попытки склеить две строки.

Молодые люди какие-то, старые… О требованиях к полигонам мы согласны в итоге или нет?

И все равно у одного из них моделька однажды дернется и скажет, что нужно 429496728 акций купить (здесь на десятичный порядок меньше, чем числа, которые можно интерпретировать, как возможно signed, и оттого все куда хуже).

Только в одном случае матожидание времени до наступления этого события — сто лет, а в другом — скажем, месяц. Что выберете?

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

Вы хотите, чтобы я тут биржевой движок целиком написал в комментариях, что ли? Моя цель — проиллюстрировать, считайте, полупсевдокодом, почему «решение купить (uint)-10 акций» не обязано вот прям сразу стриггерить лимиты.

Долго уже мечтаю увидеть статью о том, почему HFT не миф, и в каких конкретных кейсах до оптимизации на одну наносекунду винрейт был 50%, а после - 50.001%.

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

Массовое применение Haskell-я и ML?

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

Как и ответы на выдуманные вами самими вопросы.

Вопрос о массовости вами задан не был? Ну ок (тоже ирония, если что).

А клонировать-то зачем? Я могу написать

someXml :: XmlNode
someXml =
  TagNode "html"
    [ TagNode "head"
      [ TagNode "title" [TextNode "hello world"]
      ]
    , TagNode "body"
      [ TagNode "h1" [TextNode "the header"]
      , para
      , para
      ]
    ]
  where
  para = TagNode "p" [TextNode "foo", TextNode "bar"]

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

Свобода - это когда тебя не принуждают (фрилансер свободнее, чем сотрудник корпорации)

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

Формальное, теоретическое определение (Свобода- там где демократия, права человека, либерализм экономический и политический)...

Тут сначала придётся определить формально демократию и права человека.

Например, Поппер в «The Open Society and Its Enemies» определяет демократию в основном как возможность народа (бескровно?) сменить правительство. Окей, когда власть захватил тиран, отменил выборы и всё такое, вроде бы всё понятно: такой возможности нет (или есть? история знает примеры распавшихся «бескровно» тираний). Но когда де-юре есть выборы, Конституция, и так далее, и даже там кто-то что-то как-то считает на этих выборах — как тут с возможностью? Она есть, просто результаты мне или вам не нравятся потому, что ей не воспользовались и народ всё устраивает, или возможности на самом деле нет, а все «одобрямс» народа — нарисованные и дутые?

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

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

Кстати, хороший у вас код, я php не знаю, но прям… $ary ?? $this->selected as $node, если я правильно понимаю, что делает, прямо классика ООП и абьюзинга абстракций.

Рисовать на хаскеле (и на любом другом чистом языке) клонирование бессмысленно, так что я ограничусь траверсалами. Для типа XmlNode выше, например, наиболее прямым аналогом вашего кода будет что-то вроде:

data Stage = Pre | Post deriving (Eq, Ord, Show)

data WalkEntry = WalkEntry { stage :: Stage, depth :: Int, node :: XmlNode } deriving (Eq, Ord, Show)

walk :: XmlNode → [WalkEntry]
walk = go [] 0 where
  go acc depth node = WalkEntry Pre depth node : cont (WalkEntry Post depth node : acc) where
    cont acc' | TagNode{..} <- node
              , Just (_, next) <- unsnoc children = go acc' (depth + 1) next
              | otherwise = acc'

Вместо $run_close результат аннотируется перечислением Pre или Post, и конкретное поведение с $run_close = false можно получить, например, как

walkPre :: XmlNode → [WalkEntry]
walkPre = filter ((== Pre) . stage) . walk

Либо можно завернуться в произвольный моноид, и заодно одним walk описать не только получение списка нижних-правых, но и, скажем, элемент с самым большим числом детей, и так далее (если брать соответствующий моноид):

walk' :: Monoid m => (WalkEntry → m) → XmlNode → m
walk' inj = go 0 where
  go depth node = inj (WalkEntry Pre depth node) <> mid <> inj (WalkEntry Post depth node) where
    mid | TagNode{..} <- node
        , Just (_, next) <- unsnoc children = go (depth + 1) next
        | otherwise = mempty

На практике, конечно, я бы так не делал, а взял бы линзы для XML'я или JSON'а и тому подобного.

Дерево (например, бинарное, с объектами типа A только во внутренних узлах, и пустыми листьями) определяется как начальная алгебра функтора X ↦ 1 + X × A × X. Можно показать, что эта алгебра — изоморфизм, поэтому на самом деле вы имеете равенство (окей, изоморфизм, но изнутри теорката их отличить тяжело) T = 1 + T × A × T.

С таким подходом проблемы будут независимо от языка.

С каким — таким? «Посмотреть предложенный собеседником код и найти там проблемы»?

Проблемы с предложенным вами подходом потому, что он нелокален. Ну, вернее, это проблема не вашего подхода, а общей философии C++: например, если вы почитаете или послушаете гугловского Титуса Винтерса, топящего за что-то вроде «C++ design unit is an overload set» (а не функция или класс или модуль или…), и послушаете про его же кулстори про эволюцию гугловской кодовой базы, то увидите, что добрая часть проблем вылезает именно из-за этой нелокальности. Семантика, скрывающаяся за именем, оказывается размазана не по одному определению одной функции за этим именем, а по всему множеству всех функций с этим именем, потенциально в разных неймспейсах (привет ADL), и изменение этого множества приводит к забавным нелокальным спецэффектам (например, вопрос о роли requires вы почему-то пропустили — но я вас понимаю, сам бы его пропустил). Это — один из худших видов coupling'а.

Наверное, это не его забота, обеспечить приёмистость не точно соответствующих типов в предоставляемом API, — но только таких, которые разрешены проектировщиком API, а не любых, которые язык позволяет.

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

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

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

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

Как успехи с продумыванием кодовой базы на года вперёд?

Если у вас достаточно времени, то проще на том же хаскеле наваять для тех же трейдеров DSL с понятными сообщениями об ошибках, понятным поведением, понятным предметно-специфичным статическим анализом, и предсказуемой кодогенерацией. Да, проще, чем заставлять всех разбираться в экспоненциальной сложности C++.

Эта проблема не является специфичной для данного случая.

Ну, да. Но в этом случае она вылезает тоже.

Всё же, первая задача значительно проще, вы преувеличиваете.

Observationally equal. Я не знаю ни одного человека, решившего задачу останова, и не знаю ни одного человека, который бы знал C++. И, кстати, сверхвысокие HFT-шные зарплаты не позволяют их найти — все знатоки C++ в основном почему-то прячутся в комментариях на хабре или опеннете и тому подобных, вместо того, чтобы приходить на собеседования и получать свои заслуженные 500-700 в год.

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

Knight capital — это так, намёк на то, что при факапах в коде проверки и ограничители срабатывают не сразу, и даже их может оказаться недостаточно (они ж обанкротились и закрылись).

Так в коде выше на биржу уходит тоже большое число относительно мелких (если хочется, можете вместо sendOrder читать splitAndExecuteOrder или чё-т такое, сути это не меняет). Или вы думаете, что если вам надо купить/продать 100500 акций, то вы их покупаете одним пакетом? Это плохая идея по куче причин.

Ещё код по теме можно посмотреть в моём ответе

Ожидать, что люди будут в продакшене писать и поддерживать такой код — несерьёзно.

Интересно, а где в 1990-ом году это было сделано "максимально прямо"

Написано в фразе прямо перед процитированной вами, но я повторюсь: в ML (ранние 70-ые).

Более того, даже тот же хаскель (с ещё более полиморфным полиморфизмом) появился в том же 90-м.

и почему эти мощные конкуренты (если они вообще были) до массового применения так и не дошли?

Полигон — это когда массовое применение прямо там же, да.

Не двигайте гоалпосты.

Зачем их обновлять?

Потому что код меняется. Вчера const char* было, сегодня std::string_view (или наоборот). Вчера double price , сегодня fixed_point<4> price (в который неплохо бы конвертировать из литералов, кстати, но не будем вскрывать эту тему, перегрузки разбегаются как тараканы).

Вот я скопипастил ваш код (и заодно добавил requires из соседнего поинта) и сделал вид, что он эволюционирует со временем, «забыв» поменять тип первого аргумента в удалённой перегрузке:

template <typename T1, typename T2, typename T3>
  requires (!(std::same_as<T3, double> || std::same_as<T3, float>))
void sendOrder(std::string_view symbol, T1 buy, T2 quantity, T3 price) = delete;

void sendOrder(const char *symbol, bool buy, unsigned quantity, double price)
{  
}

int main()
{
  sendOrder("DAL", true, -10, 5.0);
}

Обратите внимание на -10. Задача со звёздочкой: скомпилится или нет? (ответ: да, скомпилится)

Теперь я убираю requires. Скомпилится или нет?

clang говорит «ambiguous» и ошибка, gcc компилирует (лан, моя вина — надо -pedantic добавлять), но говорит

warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   13 |         sendOrder("DAL", true, -10, 5.0);
      |         ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~

Что за worst conversion? Откуда? Сможете сходу огласить весь список conversion'ов? Почему requires так влияет? Смогли бы предсказать такой результат, просто глядя на код? Сможете объяснить среднему программисту на C++, почему результат такой и причём тут requires? Сможете объяснить среднему трейдеру, который на C++ пишет постольку, поскольку вы ему даёте апишку для вашего инфра-кода, а у себя он там обмазывается q, матлабами и прочим?

Сможете быть уверенным, что сможете адекватно поддерживать кодовую базу на миллион-другой строк, где подобными финтами всё обмазано, и где простейшее изменение рискует превратиться в сутки-другие расхлёбывания выблева компилятора минимум, и где рекомпиляция одного TU занимает минуты даже в дебаг-билде, без оптимизаций? Мне приходилось поддерживать, не рекомендую.

И это — правда, та функция, адрес которой, весьма вероятно, необходимо будет брать?

Да, а что? std::bind_front(&sendOrder, "GOOG");

Сейчас там и так double последним аргументом.
В таких местах float не используется.

Почему? Я хочу, чтобы у меня в кэшлайн помещалось 16 цен, а не 8 — почему вы мне запрещаете так делать?

Олсо, в таких местах и double не используется, а используется фиксированная точка, но неважно — это учебный пример.

В данной задаче это — необходимо?

Не, просто задача «освоить C++» примерно настолько же реализуема, как «решить проблему останова».

Вы, видимо, обознались.

Вовсе нет. Просто считать, что это число вот прям сразу пойдёт в пакетик на биржу — это несколько ошибочно. Там, например, может быть что-то по смыслу как

void sendOrder(...)
{
  const int batchSize = computeSmallBatchSize(quantity);
  for (int i = 0; i < quantity; i += batchSize) {
    sendToExch(std::min(batchSize, quantity - i));
    waitPriceToSettle();
  }
}

И если вы хотите купить 10 акций, а случайно покупаете -10 = 4294967286, то до того, как у вас сработают всякие риск-чеки, дневные лимиты, и прочее, у вас уже уйдёт ордеров на сотню тыщ акций.

Ну вроде как цпп отец раи

CLU.

в цпп есть шаблоны

ML и параметрический полиморфизм. В C++ обкатывали разве что идею «как бы параметрический полиморфизм сделать максимально криво, но чтобы потом было весело делать головоломки и отсеивать людей на интервью». Хорошо разве что для job security.

валью семантика

совсем не доделанная в цпп

Здесь согласен.

Информация

В рейтинге
1 171-й
Зарегистрирован
Активность