Обновить
1
2.3

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

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

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

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

То, что было сделано в 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.

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

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

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

Почему не было даже попыток применить данное решение?

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

Потому что теперь вы не можете взять адрес sendOrder(по крайней мере, в контекстах вроде аргумента для стандартных алгоритмов, где ожидаемый тип callable не фиксирован).

Потому что если вы захотите принимать double или floatпоследним аргументом, то вам уже придётся обмазываться концептами и писать что-то вроде requires (!(std::is_same_v<T3, double> || std::is_same_v<T3, float>)). Очень удобно и дружественно к людям, в чьи обязанности входит не только штудирование талмуда++. Кстати, тут крошка-джун пришёл к отцу, и спросила кроха: «папа, а что тут лучше, std::same_as или std::is_same_v? Какая вообще между ними разница? Какой-какой частичный порядок? Я полностью каждый день порядок навожу!»

следовало бы научиться пользоваться C++

И решить проблему останова на сдачу.

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

Человек, отвечающий подробное, даже не понимает смысла учебных, иллюстрирующих примеров.

Knight capital, кстати, передаёт привет.

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

Зависимые типы помогут (и помогают).

Информация

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