Беседы о функциональном программировании на C++ Siberia 2019 +33



Всем привет!


Недавно в Новосибирске прошла очередная C++ Siberia 2019. На конференции была уютная атмосфера и много хороших докладов. Пользуясь случаем, я побеседовал с двумя нашими докладчиками, которых совсем скоро вы сможете увидеть и в Москве.


Иван Чукич — один из разработчиков KDE, преподаватель и исследователь дизайна языков программирования в Белградском университете.


Александр Гранин (graninas) — известный спикер и разработчик, специализирующийся на ФП, организатор новосибирского ФП-сообщества LambdaNsk.



Сергей: Всем привет, давайте знакомиться. Александр — кейноут-спикер на этой C++ Siberia, а Иван был кейноут-спикером в прошлом году. Давайте поговорим о функциональном программировании. Насколько помню, ФП в C++ было темой твоего предыдущего кейноута, Иван…


Иван: Не целиком, но частично — да.


Сергей: А у Александра тема доклада именно о функциональном программировании. Поэтому я подготовил парочку вопросов, и первый — как вы определяете ФП?


Александр: Я не думаю, что существует какое-то одно «правильное» определение ФП, но если говорить лично обо мне, то ФП — это нечто с функциональной композицией и функциями первого класса.


Иван: Я согласен, но добавил бы ещё функции высшего порядка — те, которые могут принимать другие функции в качестве аргументов и возвращать как результат.


Cергей: Ссылка на функцию в Си — считается?


Иван: Нет, Си не является функциональным языком программирования :-)


Сергей: Расскажи, почему?


Иван: Потому что нельзя из комбинации указателей на функции создать новую функцию, можно только указывать на уже существующие. Конечно, если в ход не пошли какие-то ассемблерные хаки.


Сергей: Отлично, теперь у меня есть официальный ответ! Люди постоянно задают этот вопрос, почему Си — не язык функционального программирования, ведь там есть полноценные функции. А вот почему C++ является функциональным языком, уже понятней…


Александр: Я бы не сказал, что C++ — это прямо-таки функциональный язык программирования, он поддерживает кучу парадигм, влитых в один язык.


Сергей: Имелось в виду — C++ поддерживает функциональную парадигму, конечно. Кстати, почему? Он поддерживает потому, что можно манипулировать функциями высшего порядка?


Иван: Ну, мне кажется, он всегда таким был, ведь даже в C++98 у функции высшего порядка уже имеются, даже в STL. Это не самый удобный функциональный язык — есть языки, которые реализуют ФП и получше. Но для моих нужд он всегда был достаточно функциональным.


Сергей: А вот с этого места поподробней. Что у тебя за нужды такие?


Иван: Это сложно. Давай, расскажу историю. Когда я учился в университете, мы проходили LISP, и все ненавидели этот LISP, потому что он уродливый. Но что я из него понял, это как симулировать конструкции Си прямо в коде на LISP. А потом однажды вышла новая версия Java, которую выпустили ради вещей вроде встроенного цикла foreach, и я подумал: ого, вам нужен новый компилятор, новая версия языка и всё такое новое только для того, чтобы реализовать нечто, что я делал в университете на LISP, который вообще-то даже не поддерживает циклы. В тот момент я осознал, что ФП — довольно приятная штука для построения высокоуровневых абстракций, и вот поэтому я использую функциональный C++ в 2019 году.


Cергей: По сути, ты используешь ФП для высокоуровневого дизайна.


Иван: Точно.


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


Сергей: Ну, ты можешь использовать ФП не только с C++, правда? Хорошо, тогда следующий вопрос: какую часть C++ вы используете? Если важны только вопросы дизайна, можно выбрать только ту часть, которая хорошо сочетается с ФП, ту, что вы реально используете.


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


Сергей: Да, мы уже поняли, что лямбды тебе очень нравятся :-)


Иван: Не то чтобы они нравились очень, но это явно лучше всего, что у нас было в C++98, с ними удобней работать.


Александр: Да, мне тоже нравятся лямбды — это фича настолько универсальная, что можно было бы писать только на них одних. Это что-то вроде универсального комбинатора, позволяющего конструировать любую логику — может, не так красиво, как в других языках, но не менее полезно.


Сергей: Иван, ты тут отметил, что есть стандарты до C++11, например — C++03, весьма распространённый, и там уже есть функциональные фичи. И функциональных фич стало больше в новых стандартах… Можно ли утверждать, что C++ движется в сторону ФП? Продолжится ли это движение, или прекратится? И к чему тогда приведет?


Иван: Есть хороший доклад Simon Peython Jones о языках программирования в целом, и он там рисовал граф, показывающий множества безопасных языков и используемых языков. Haskell начал свою историю как совершенно безопасный язык, с которым нельзя ничего сделать, — потому что там нет I/O, и вообще ничего такого. SPJ отнес язык Си и языки ассемблера к тем, которые очень полезны, но вместе с тем и чрезвычайно небезопасны. С тех пор Haskell начал двигаться в сторону большей безопасности. С другой стороны, те фичи, которые появляются в C++ — они появляются, в основном, для увеличения безопасности, чтобы можно было писать корректные программы более просто. Так случилось, что большинство этих вещей приходят из языков функционального программирования.


Сергей: Как думаешь, почему так? Из-за самой природы ФП?


Иван: Да, может быть, по самой природе… но я не знаю точно.


Александр: Думаю, что ФП так захватывает всех просто потому, что мы устали от борьбы с шаблонами, с переставлением байтиков, с какой-то низкоуровневой мутью — нам хочется что-то достойное, чтобы приложить собственный интеллект.


Сергей: То есть, для тебя это что-то вроде интеллектуального упражнения?


Александр: Да, типа того.


Сергей: Понимаю. Куда как интересней разработчику думать о высокоуровневых абстракциях, чем реализовывать тупые стандартные фичи — это неприятно. Тогда такой вопроС: есть ли у вас опыт практического применения ФП в C++? Какие-то проекты продакшене?


Иван: Конечно. Один из самых больших в мире проектов, KDE, имеет внутри себя несколько частей, интенсивно использующих функциональный стиль. Конечно, это смесь, как бы так сказать, более традиционного объектно-ориентированного C++ с набором концепций из функциональщины. Я никогда не собирался быть пуристом или чем-то таким. Я всегда стараюсь объединять лучшее из разных миров.


Сергей: А что насчет Haskell или Scala? Они ведь широко используются в продакшене. Как вам такая идея, что Haskell сейчас считается эталоном функционального языка? Это особенно отмечают пуристы.


Иван: Да, я согласен, что Haskell на сегодняшний день — это синоним ФП. По сути, любая фича из Haskell воспринимается людьми как нечто, относящееся к ФП. Это не обязательно правда, но думаю, что Haskell действительно стал самым популярным академическим языком функционального программирования. Знаю, что несколько банков в Лондоне и Северной Европе широко используют Haskell, но всё же Scala в данный момент куда более популярна.


Александр: Согласен, что Scala более популярна, но Haskell выглядит более функциональным языком, большинство его фич реализовано более корректно. То есть, когда у тебя есть каррирование, которое просто делать, когда есть простой способ сделать композицию, программировать становится легко и просто, это как гулять по лесу и наслаждаться видами.


Иван: Но иногда в лесу есть медведи. Если вы в России.


Сергей: Как думаете, C++ в основном черпает вдохновение тоже в Haskell? Стоит это делать?


Иван: Ты намекаешь на каких-то конкретных участников комитета? :-) Кто-то намекал, что концепты появились как результат осмысления тайпклассов, но Бьёрн прекратил эти слухи и даже написал какой-то документ о том, в чём концепты отличаются от тайпклассов. С моей точки зрения, они отличаются всем, но служат одной цели. Просто разные подходы.


Сергей: А future/promise как-нибудь связаны с ФП? Кажется, Бартош утверждал, что это плохо реализованые монады.


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


Сергей: А нужна ли нам улучшенная поддержка монад в C++?


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


Иван: Кстати, раз уж мы на конференции, давай я тебе тоже задам вопрос, Сергей. Ты сказал, что конференции — это увлекательная работа. Поделись, чего в ней такого интересного, и посоветовал бы ты мне или товарищу Гранину самостоятельно организовать конференции в других частях мира?


Сергей: Организация конференций — это действительно круто, ты встречаешься с множеством интересных людей, но это только вершина айсберга. А там внизу — куча работы, вся эта подготовка зала, еда для участников, не говоря уж о поиске спикеров. C++ Russia — всё ещё не самая известная в мире конференция, и докладчикам приходится объяснять, что мы — новая конференция, что здесь происходят интересные вещи. Нужно убедить докладчика, особенно известных звездных докладчиков, которым не особо интересно лететь в Россию только чтобы посмотреть новую страну. Организаторская работа — тяжелая, в особенности если ты работаешь еще и на основной работе. Но всё окупается общением с этими замечательными людьми. Тем не мене, я сейчас дошёл до того, что скорее я побываю на чьей-то чужой конференции, чем буду делать свою.


Иван: То есть, ты предлагаешь посещать конференции, а не делать их.


Сергей: Да, если можно избежать организации конференции — стоит воспользоваться шансом. При организации на тебя свалится огромный груз, это как ещё одна 8-часовая работа. Лично я примерно за 3 месяца до конференции начинаю работать дополнительные 8 часов в день. Делать это весело… но надеюсь, моей семье так же весело. Спасибо что спросил!


А теперь, назад к теме. Мы говорили о функциональном программировании, и вы почти убедили меня, в смысле — ваши доклады убедили. Есть подозрение, что функциональных подход в C++ поможет мне с многопоточностью, когда нужно синхронизировать разные вещи. Правда ведь поможет?


Иван: Конечно.


Александр: Несмотря на то, что у меня ограниченный опыт работы с функциональной многопоточностью конкретно в C++, имею сказать, что когда у тебя есть мир чистых функций, о них куда проще рассуждать в многопоточной среде. Если ты пишешь логику, например, конкурентную, можно не задумываться о синхронизации всех этих штук, о мьютексах, о критических секциях, о чем угодно. Всё это просто исчезает, поскольку ты думаешь о коде как об обычном последовательном коде, а вся многопоточность и синхронизации прячутся где-то внутри. Есть куча подходов и к многопоточному программированию, и к функциональному. Я не уверен, что так происходит в совершенно всех подходах, но например, software transactional memory — отличный подход для уменьшения сложности в конкурентных приложениях. К сожалению, это вопрос выбора правильных компромиссов.


Иван: Когда компилятор делает за тебя всю работу, за это приходится платить эффективностью.


Александр: Ну, есть разные проблемы. Для начала, нужно разобраться во всех этих вещах вроде STM, и дальше передать это тайное знание коллегам. А потом найдутся ошибки в конкретной реализации и места, которые могут работать сильно лучше при использовании ручного управления тредами. Зато вы сможете писать быстрей и проще, чем при таком ручном управлении. Выполняться будет медленней, но зато код будет с меньшим количеством ошибок. Кстати, Иван, что ты думаешь о том, насколько этот вопрос хорошо освещен на конференциях?


Иван: Эта тема процветает. За последние годы все большие конференции — CPPConf, C++ Russia, Meeting C++, и т.п. — получили доклады или напрямую о ФП, или об алгебраических структурах данных, или о чём-то таком. Иногда докладчики даже не подозревают, что в своём докладе они рассказывают о какой-то концепции из ФП. В C++ приходят вещи из самых разных мест… Люди обычно не пишут только на одном языке. Представим Ивана Иванова, работающего над проектом, написанном на Erlang и C++. Потом приходит Татьяна Петровна, и она уже работает на Haskell с чистыми функциями и всем таким, они берут свои любимые механизмы и портирует их в C++, и в результате огромное количество людей из разных сообществ привносят в C++ всё новые и новые вещи. Всё это происходит у нас на глазах. Как минимум, это происходит в сообществе разработчиков C++, а вот насчёт C++-компаний я не столь уверен. Тем не менее, множество людей из C++-сообщества сейчас работают над функциональными концепциями.


Александр: Я правильно понял, что множество топовых C++-разработчиков изучают Haskell только для того, чтобы понять, что творится с C++?


Иван: Не уверен, что они учат Haskell именно по этой причине. Думаю, C++-разработчики просто очень эгоистичные. Они так хорошо изучили C++ только потому, что он сложный. И если ты хочешь выучить что-то действительно новое, твой путь лежит явно не в какую-нибудь Java, которая специально создана быть простой. Тебе нужно искать в области необычных и странных языков, самых странных, и Haskell автоматически окажется среди самых популярных ответов. Человек видит его, понимает: о, это что-то более сложное, чем C++, нужно выучить. Когда я изучал Haskell, со мной было то же самое, и у меня есть знакомые, которые прошли точно по такой же цепочке рассуждений.


Александр: Когда Эрик Ниблер был у нас в Сибири и показывал свою библиотеку для ренжей, его часто спрашивали — что же было источником вдохновения. Он отвечал, что это Haskell. Может быть, все фичи подряд из него брать не стоит, но некоторые явно нужны в сообществе.


Иван: Это что-то вроде эволюции. Генетический материал. И Haskell тоже можно улучшить, взяв что-то из C++. Большинство языков сейчас проходят такую эволюцию. Java попыталась приспособить себе LINQ из C#, а создатели LINQ из C# черпали вдохновение в Haskell, и т.п. Получается такая красивая запутанная сеть взаимовлияния между разными языками.


Александр: Тем не менее, C++ всё ещё низкоуровневый язык?


Иван: Большинство считает, что да.


Сергей: О какой «низкоуровневости» вы сейчас говорите?


Иван: Компилируется в низкоуровневый код. Но при этом работает с высокоуровневыми абстракциями. Весь смысл и цель в том, чтобы такие абстракции не приводили к излишним накладным расходам в смысле производительности. C++ должен генерить из своих абстракций такой код, который был бы не хуже написанного вручную. По крайней мере, теоретически.


Александр: Что будет, если кто-то нарушит это правило? Например, ренжи.


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


Сергей: Тормозят не сами ренжи, а ренжи в режиме отладки — в этом суть всей дискуссии. В релизном режиме они работают отлично.


Иван: Это нормально.


Сергей: Не все с этим согласны :-)


Иван: Да, знаю на практике. В одной компании, в ее самой часто используемой библиотеке… не буду говорить, что это за компания и библиотека… есть алгоритмы, которые асимптотически существенно медленней в отладочном режиме. И кто теперь будет жаловаться, что ренжи делают всё то же самое?


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


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


Сергей: Haters gonna hate.


Иван: Именно. Например, все используют сортировку. Просто представьте, что в стандартной библиотеке больше нет сортировки. Как бы вы её реализовывали?


Сергей: Я такой вопрос обычно задаю на интервью :-) Это очень частый вопрос.


Иван: Да, частый вопрос — как реализовать сортировку. И дальше ты рассказываешь какую-то базовую версию быстрой сортировки (quick sort), которая на самом деле не используется вообще нигде в мире, потому что она может быть очень медленной в определённых случаях. Стандартная библиотека и не позволяет её использовать, потому что она требует гарантированного N log N, а быстрая сортировка так не может. По большей части может, но стандарт в этом месте очень жесткий, и не говорит об аккумулированном N log N, это должно быть чистое N log N, и как ты будешь реализовывать такой алгоритм? Нужно провести исследование, найти множество разных оптимизаций быстрой сортировки, и слить их в один алгоритм, состоящий по крайней мере из трех разных алгоритмов, как это сделано в libstdc++. В этом и есть смысл стандартных библиотек — тебе не нужно знать все эти вещи, чтобы программировать. Не нужно разбираться, как реализовать всё на свете наиболее эффективным способом, кто-то другой уже позаботился за тебя об этом. Поэтому мне и не нравится такой подход, когда люди заявляют: «STL — очень сложная, давайте не будем её использовать и напишем всё с нуля вручную».


Сергей: Мы подходим к концу интервью, поэтому последний вопрос: как вам в России?


Александр: Похолодало.


Сергей: Даже для тебя? Ты же местный!


Иван: А для меня здесь куда теплей, чем ожидалось.


Сергей: Сейчас -16, а мы сказали тебе, что будет -40.


Иван: Да, вы обещали! Я специально готовился к минус сорока. А потом смотрю на градусник, а там всё теплей и теплей.


Сергей: Ну что ж, теперь мы встретимся только на C++ Russia 2019, это будет в Москве, и там будет плюсовая температура. Спасибо за интервью и до встречи!


Минутка рекламы. 19-20 апреля пройдёт конференция C++ Russia, на которой Иван выступит с докладом «Move-only C++ design», а Александр расскажет про монадические парсеры. Кроме того, Иван проведёт один из трёх больших тренингов«Applied functional programming in C++». До начала конференции остаётся месяц и программа продолжает уточняться. На официальном сайте можно посмотреть, какие доклады уже попали в программу и приобрести билеты. Обратите внимание, что билеты бывают разных типов, и выбрав правильный, можно существенно сэкономить.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (7):

  1. fukkit
    /#19907384 / +1

    Сергей: А нужна ли нам улучшенная поддержка монад в C++?
    Александр: Безусловно, это наиболее важная фича, которая может превратить C++ в действительно хороший язык.

    А может превратить чтение С++ кода модного хипстера нормальным специалистом в ад.
    Идеи вида: «Я поездил на троллейбусе и теперь считаю, что каждому автобусу нужны рога».

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

    • olegchir
      /#19907480

      > когда его дальнейшая доработка и поддержка встанут тупо дороже.

      или встанут тупо дешевле, учитывая что у чистого кода сайдэффектов меньше, и одна из основных статей расходов поддежки — нелокальные эффекты рефакторинга — существенно удешевляется за отсутствием таковых

      • fukkit
        /#19908832

        Основные статьи расходов поддержки — впиливание бизнес-фич, не предусмотренных изначальным дизайном. А с этим у функциональной парадигмы ничуть не краше остальных.
        Грамотно реорганизовать систему типов под новый функционал зачастую ничуть не проще, чем иерархию объектов.

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

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

    • Antervis
      /#19909642

      А может превратить чтение С++ кода модного хипстера нормальным специалистом в ад.
      Идеи вида: «Я поездил на троллейбусе и теперь считаю, что каждому автобусу нужны рога».

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

  2. ov7a
    /#19907542

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

    Чувак про Чёрча слышал вообще?

    • graninas
      /#19910282

      И даже вписал его в стихи :)

      Спустилась холодная ночь. Где-то в чате публичном
      Собрались любители типов, сторонники Черча и Карри.
      И в свете ночных фонарей за пределами окон столичных
      Держали военный совет хаскеллисты. О быстром ударе
      Они размышляли и строили планы безумной атаки.
      Хотели они развенчать динамичных соблазнов коварство,
      Фальшивых посылов разрушить устои и к-ложные знаки,
      А после победы устроить теории типов главенство.

  3. Augustxeno
    /#19907564 / -1

    что там насчет низкого уровня языка было сказано