понедельник, 26 октября 2015 г.

Scala и функциональное программирование

Пару месяцев назад мне довелось изучить ещё один язык программирования, Scala. И поскольку Scala это прежде всего функциональный язык (хотя тут с какой стороны посмотреть), сегодня речь пойдёт о функциональном программировании.

Я познакомился с функциональным программированием много лет назад в университете. При обучении использоваться Lisp (кто не знает что это, представте эту фразу в круглых скобках плюс ещё 100500 таких же, причём вложенных друг в друга). Я как-то спросил преподавателя, а зачем оно, это функциональное программирование? Преподаватель ответил, что во многих случаях код становится в разы короче. Ответ мне понравился, я сдал экзамен и забыл про это функциональное программирование на долгие годы.

Сейчас термин «функциональное программирование» (далее просто ФП) у всех на слуху. В значительной степени это своего рода buzzword, но без рациональности не обошлось. После десятилетий прозябания в академической среде и узкоспециализированных областях ФП вдруг нашло реальное применение. Как все хорошо знают, уже долгое время процессоры перестали улучшаться качественно (прекратилось увеличение частоты), вместо этого они стали улучшаться количественно (больше ядер в процессоре), что привело к большой востребованности многозадачности и многопоточности. Кто писал многопоточные приложения (а кто их не писал?) хорошо знаком со всеми проблемами синхронизации - семафоры, мьютексы, synchronize, потоки, асинхронный доступ и тому подобное. Бизнес логика обрастает кучей дополнительного кода, на создание которого уходит время. Потом время уходит на отладку, устранение проблем, а может быть и на модификацию, но чаще всего разработчики поднимают руки ладонями вперёд и говорят «Работает, и не́чего трогать!». С другой стороны представьте себе код бизнес логики, который нужно распараллелить; вы его просто запускаете и он работает сам по себе в несколько потоков, без синхронизаций, багов, костылей. Вот простой пример на Scala, умножающий все элементы массива на 2:

val result = Array(1, 2, 3, 4, 5).map(_ * 2)

А вот то же самое, но в многопоточном варианте:

val result = Array(1, 2, 3, 4, 5).par.map(_ * 2)

Сложно поверить в то, что всё так просто, но в большинстве случаев это действительно так. Секрет этой особенности ФП кроется в феномене под названием «чистая функция» (pure function). Это такая функция, которая не имеет побочных эффектов (side effects). Впрочем давайте не будем здесь про теорию, она очень скушная. В итоге всё работает само собой, и этого достаточно. Правда работает при условии, что код написан исключительно в декларативной парадигме. После многих лет императивного программирования сложно перейти на декларативное, но здесь проявляется большое преимущество Scala перед другими языками: насколько этот язык декларативен, настолько же он и императивен. Поначалу его можно использовать как «лаконичную Java», постепенно внося всё больше и больше функциональной составляющей в свой код.

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

Комментариев нет:

Отправить комментарий