Хабрахабр

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

Всем привет!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Иван: Точно.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть