LINUX.ORG.RU

Pattern matching для С++20

 


0

3

Посоны, что-то я упоролся по хардкору и стал писать паттерн матчинг для С++20. Пример:

variant<int, string> v = 1+2+3+5+7+11;
const auto pattern = pm::any_of(29, "42");

cout << *match(v,
  // match with pattern although pass (fall through). also create side effect in the middle of matching
  pass(pattern,            [&v] { cout << "How does this work?\n"; v = "42"; })
| pass(pattern,            [&v] { v = R"(I wonder ¯\_(ツ)_/¯)"; }) // match with pattern again, also pass
| with(pm::type(string{}), [&v] { return get<string>(v); }) // match with stored type
| with(__,                 []   { return R"(('・ω・'))"s; })) // catch-all condition (default)
<< endl;

Потом я начал писать концепции и понял, что я ещё даже не начинал упарываться. Но вроде что-то получается и довольно просто реализуется, что меня и смущает. Может я чего-то не понимаю или упустил что-то важное и сделал где-то ошибку которую я не вижу.

Я так почитал пропозал для паттерн матчинга, вроде много чего из фичей есть кроме некоторых вещей, хотя я не до конца понимаю всё, что там написано и зачем оно там.

Я это отложу пока что потому мне это надо осмыслить, но может кто-то из многоуважаемых регистрантов или не менее многоуважаемых анонимусов посмотрит пока на это барахло и что-то посоветует? В код может кто-то глянет, всё ли по феншую? Там не много, меньше 700 строк кода пока что и очень простой дизайн.

Ссылка: https://bitbucket.org/alekseyt/pm

Более полный пример использования: https://bitbucket.org/alekseyt/pm/src/master/examples/example.cpp

И там в тестах всякие guard(), within(), maybe() и т.д. Заранее спасибо.

как же уродски эти плюсы выглядят.

anonymous
()

Я так почитал пропозал для паттерн матчинга

ссылку на него тоже лучше предъявить

seiken ★★★★★
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

императивные языки к такому неготовы по дизайну

Императивный ML к такому был готов еще в прошлом веке.

anonymous
()

if/switch действительно ничем не хуже. Какие проблемы решает пм(вообще и в c++ в частности)?

anonymous
()
Ответ на: комментарий от hateyoufeel

Наверное ничем. Mach7 хорош, но на макросах.

Но вообще тот паттерн матчинг который до С++20 ничего не будет знать например про ренжи. Я вчера допёр, что в any_of надо пизать не несколько значений, а сразу ренж. Чтобы match(x, with(any_of(y))) работал как find и тогда если х - это значение, а у - это вектор, то будет поиск по вектору, а если х - это пара, а у - это словарь, то может быть поиск по ключ-значение, но может быть и как поиск по ключу, так и поиск по значению если использовать __. Но эту мысль ещё надо обдумать.

А так конечно ничем не лучше.

anonymous
()
Ответ на: комментарий от pathfinder

Я в целом согласен и с тем, что польза преувеличена, при наличии обощённых алгоритмов, и ифы не хуже. Но было интересно узнать насколько это сложно реализовать в С++ и пока получается не очень сложно. Там ещё есть всякие разные алгоритмические штуки до которых я пока не добрался и наверное не доберусь, но они могут быть полезны для С++ в целом, если компилятор внезапно научится делать какие-то новые оптимизации и они заедут в другие части алгоритмов.

Это тоже интересный вопрос: должен ли any_of быть реализован на find или find на any_of? Или find времени компиляции на any_of, а any_of времени выполнения на find? Довольно интересная тема.

aleksey_tulinov
() автор топика
Ответ на: комментарий от pathfinder

По мне, польза от pattern matching сильно преувеличена.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от pathfinder

В прологе годный pattern matching - можно матчить частично определённые переменные и их постепенно вычислять:

?- length(L, Len1), Len2 #= Len1 + 3, length(L2, Len2), L=[Len2|_].
L = [4],
Len1 = 1,
Len2 = 4,
L2 = [_102867808, _102867814, _102867820, _102867826] .
Leron ★★
()
Ответ на: комментарий от Leron

В прологе годный pattern matching - можно матчить частично определённые переменные и их постепенно вычислять:

Так, решение некой задачи есть, теперь осталось понять что это была за задача.

anonymous
()
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от aleksey_tulinov

есть разница в реализовать и реализовать хорошо и эффективно для общего случая.

anonymous
()
Ответ на: комментарий от DarkEld3r

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

anonymous
()
Ответ на: комментарий от aleksey_tulinov

в стартовом сообщении тебе примеров мало? Ну ок, сходи посмотри на другие реализации в пределах существующих реалий.

anonymous
()
Ответ на: комментарий от anonymous

Ты, банкивский петух, давай, сперва почитай определения, что такое «функциональный язык программирования» и «императивный язык». А то начал скакать, не имея понятия.

anonymous
()

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

Jetty ★★★★★
()
Ответ на: комментарий от Jetty

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

aleksey_tulinov
() автор топика
Ответ на: комментарий от anonymous

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

anonymous
()
Ответ на: комментарий от anonymous

Знатно ты пригорел.

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

Давай приводи определения

Нет, ты. Ты начал загоняться про функциональность и императивность - ты и отвечай за свои слова.

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

anonymous
()

Вроде получилось добавить немного сахара для читаемости:

variant<int, string> v = 1+2+3+5+7+11;
constexpr auto pattern = pm::any_of(29, "42");

match(v
  | pass(pattern,         []   { cout << "How does this work?\n"; })
  | pass(pattern,         []   { cout << R"(I dunno ¯\_(ツ)_/¯)" "\n"; })
  | with(pm::type(int{}), [&v] { v = "42"; return get<string>(v); })
  | with(__,              []   { return R"(('・ω・'))"s; }));

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

Вот я сравниваю со скалой:

x match {
  case 0 => "zero"
  case 1 => "one"
  case 2 => "two"
  case _ => "other"
}
match(x
| with(0,  [] { return "zero"; })
| with(1,  [] { return "one"; })
| with(2,  [] { return "two"; })
| with(__, [] { return "other"; }));

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

aleksey_tulinov
() автор топика

Я боюсь, что толкового сопоставления с образцом в C++ уже не будет никогда. ООП мешает с деструкторами и исключениями (попробуйте-ка enum создать сложнее hello world со вложенными классами). Если бы могли, то давно бы добавили. Увы, C++ - это уже отстающий поезд.

anonymous
()
Ответ на: комментарий от anonymous

развивая извращения

match(x)
  | with(1) > [](){ return 1; }
  // или так
  | with(2) >> [](){ return 1; }
  // или так
  | with(__) <=> [](){ return 1; }
  // или вообще вот так
  | with(__) --> [](){ return 1; }
anonymous
()
Ответ на: комментарий от anonymous

match(x | … ) –> match(x) | …

Думал об этом, но пока не вижу как это может что-то улучшить. Это не совсем так как в ренжах. Собрать всё выражение целиком таким образом можно, но потом это выражение надо запустить и получить результат. Вызов match() как раз всё выражение запускает и возвращает результат. Если он этого делать не будет, то надо будет делать что-то вроде (match(x) | ... )() чтобы его запустить.

Но идея неплохая, match(x) может однозначно создать выражение из значения без плясок вокруг неоднозначности | для значение | условия и условия | условия.

Может быть что-то вроде *match(x) | ... может сработать. Я эту мысль ещё раз обдумаю, спасибо.

aleksey_tulinov
() автор топика
Ответ на: комментарий от aleksey_tulinov

Собрать всё выражение целиком таким образом можно, но потом это выражение надо запустить и получить результат.

Ну и пусть будет выражение. Результат будет при касте выражения к типу результата. Или при явном вызове, например, get(match(x) | ...)

anonymous
()

Чем это лучше божественного _Generic?

anonymous
()
Ответ на: комментарий от anonymous

Так а что это даст? Это же ничего не сэкономит, только добавит скобок или других символов. А каст к типу выражения без проверки результата - это UB, std::optional в результате match() от этого защищает, его нельзя скастовать неявно, надо либо разименовать безусловно, либо проверить и тогда уже разименовать.

Более «чистый» синтаксис в пропозале. Там что-то вроде краткого синтаксиса для лямбд который работает внутри inspect. У языковой поддержки паттерн матчинга есть явные преимущества, и это одно из них. Другое - это то, что компилятор будет видеть весь контекст выражения целиком, а не так, что есть отдельно контекст условия, отдельно контекст выражения, отдельно контекст выполнения выражения. Третье - это то, что обработка ошибок в компиляторе будет гораздо лучше.

А pm наверное ближе к лиспу и Trivia, но эксплуатирует некоторые фичи С++ чтобы сделать синтаксис более читабельным. Ну я так думаю, может кто-то не согласится. Ну и кое-что всё таки не так как в тривии сделано, но это наверное ошибка.

aleksey_tulinov
() автор топика

Наверное финальная версия на сегодняшний день: https://bitbucket.org/alekseyt/pm/src/master/README.md

Из интересностей, вот это ошибка компиляции:

pm::match("abc"
  | pm::with(pm::any_of(1, 2, 3), [] {}));

А это не ошибка компиляции:

pm::match("abc"
  | pm::with(pm::any_of(1, 2, "3"), [] {}));

Разница в том, что в первом случае pm «видит», что матчится строка с числами, а значит результат всегда false, и прямо во время компиляции генерирует ошибку.

А вот так опять компилируется:

pm::match<false>("abc"
  | pm::with(pm::any_of(1, 2, 3), [] {}));

Довольно забавно. Можно было бы ещё нафантазировать какие-нибудь оптимизаторы и валидаторы, но я этого делать наверное не буду, в принципе то, что есть и так отвечает на те вопросы которые у меня были.

aleksey_tulinov
() автор топика

pm в фп-дристне это следствие отсутвия полиморфизма и наличия убогой, примитивной системы типов.

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

Ну и захват x из внешнего контекста - говно. Передавай уточнённый тип аргументов в функцию.

anonymous
()
Ответ на: комментарий от anonymous

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

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

anonymous
()

Не вижу destructiring. PM без destructuring не нужен.

Scala:

person = new Person("John", "Brown", 18);
person match {
    case Person(firstName, _, _) => print("Hello " + firstName + "!")
}

dimgel ★★★★★
()

А что в этом сложного-то?

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

И вы смеетесь над хаскеллем еще. Вообще, я не понимаю фанатов C/C++. Они говорят о производительности программ намного больше, чем о самих программах. А смотрим в код любого C/C++ проекта - инженерное болото, совершенно нечитабельное. Зачем? Нет чтобы продумать структуру программы, написать все чисто и красиво, без сложных конструкций - нет, у нас тут лучше будут темплейты и метапрограммировние.

И во всяких лиспах то же самое. Везде то же самое. Зачем? Почему нельзя как на бумаге чисто и понятно код писать? У нас сегодня только ОДНА свободная OS в мире - Unix. Ну или вселенная свободных OS. Миллиарды человекочасов вложено в это неподдерживаемо(in principle) и непереносимое творение. И где ваша свобода тогда? Зачем открывать код, если вы его никогда не прочтете, не замените, ничего не сделаете. Если единственно, что ваших силах - принять что вам дают и смириться? Программирование пошло по слепой дороге. Программисты стали рабами машин.

Еженедельно я вижу, как здесь фанатов математического программирования называют идиотами. Ежемесячно тут возбуждаются от свободного железа и маломощных, упрощенных компьютеров. И ежедневно вы утыкаетесь глазами в экран и жадно обсуждаете эту инженерную гонку за новыми технологиями и прогрессом. Гонка, которая уже создала тысячи противоречивых стандартов, тысячи спецификаций, десятки тысяч языков програмирования, десятки тысяч разных API и прочей бюрократии. И которая воздвигла себе как венец творения, как алмаз инженерной мысли C++ - монструозное подобие, в которое годами лямбды и паттерн-матчинг добавить не могут. Почему это вообще общественнпая повестка? Это элементарные, детские вещи, которые каждый может написать сам, если ему надо. Но в вашей среде это сделать тяжело.

И все, что написано сегодня, будет выброшено через 20 лет. Потому что там, где дожна быть чистая математическая функция, чистый алгоритм(State которого не вытекает в другую систему), у вас State, потому она из абстракции становится куском железа, конкретным и материальным. Когда что-то есть только внутри, снаружи его нет. Когда оно вытекает и образует отношения с внешним миром - оно становится материальным. И уже нельзя пройти сквозь него, надо перешагивать. Уже нельзя ступить в то пространствос где оно. занято.

Вот и вся суть императивного стиля программирования.

anonymous
()
Ответ на: комментарий от anonymous

И все, что написано сегодня, будет выброшено через 20 лет.

Не трудно предугадать суть постов о вашем суждении.

anonymous
()
Ответ на: комментарий от dimgel

Destructuring делается через создание тупла:

struct S {
  int x = 42;
  string s;
} const s;

match(tie(s.x, s.s)
  | with(make_tuple(42, __), [] {});

Колбек получит заматченый тупл. В SimpleMatch для этого есть сахар, он вызовет std::apply(tuple) и тупл сразу разберётся на компоненты которые передадутся параметрами в колбэк. Я посчитал это лишним шагом, но возможно это и стоит сделать.

Просто во времена SipleMatch не было structured bindings которые и так умеют разбирать туплы, а в С++20 уже есть: auto& [firstName, _, __] = tuple;. Мне кажется, что композиция с существующими и будущими фичами языка - это более перспективно, чем добавление исключений для сахарка, но надо ещё разок это обдумать. В принципе не вижу особых препятствий для того чтобы перегрузить вызов колбэка для туплов.

aleksey_tulinov
() автор топика
Ответ на: комментарий от anonymous

Про списки чёт и не вспомнил. (Да и скучный это кейс.) В голове трансформации AST крутились, вот и сочинил пример с именованным классом.

Но конечно это всё теория, а на практике вот с этим не поспоришь:

как же уродски эти плюсы выглядят.

dimgel ★★★★★
()
Ответ на: комментарий от anonymous

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

aleksey_tulinov
() автор топика
Последнее исправление: aleksey_tulinov (всего исправлений: 1)
Ответ на: комментарий от dimgel

Паттерн-матчинг - это про красоту алгебраических типов данных, сопоставление простых (лексических) шаблонов-образцов, как частичное определение функции в хаскелеfactorial 0 = 1, и тп, а не вот это вот извращение «разложение через создание».

anonymous
()
Ответ на: комментарий от anonymous

И это всё тоже теория. :) А на практике PM – это просто синтаксический сахар. Сабж на сахар не тянет ни с какого боку, поэтому не нужно.

dimgel ★★★★★
()
Ответ на: комментарий от dimgel

А на практике PM – это просто синтаксический сахар.

И в теории - это просто лексическое сопоставление выражений. А в с++ не все так просто с этими выражениями, не говоря уже о сопоставлении этих выражений.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.