Функциональный язык программирования примеры
Основные принципы программирования: функциональное программирование
Основные принципы программирования: функциональное программирование
Если вы такой же разработчик, как и я, то наверняка сперва изучали парадигму ООП. Первым вашим яыком были Java или C++ — или, если вам повезло, Ruby, Python или C# — поэтому вы наверняка знаете, что такое классы, объекты, экземпляры и т.д. В чём вы точно не особо разбираетесь, так это в основах той странной парадигмы, называющейся функциональным программированием, которая существенно отличается не только от ООП, но и от процедурного, прототипно-ориентированного и других видов программирования.
Функциональное программирование становится популярным — и на то есть причины. Сама парадигма не нова: Haskell, пожалуй, является самым функциональным языком, а возник он в 90-ых. Такие языки, как Erlang, Scala, Clojure также попадают под определение функциональных. Одним из основных преимуществ функционального программирования является возможность написания программ, работающих конкурентно (если вы уже забыли, что это — освежите память прочтением статьи о конкурентности), причём без ошибок — то есть взаимные блокировки и потокобезопасность вас не побеспокоят.
У функционального программирования есть много преимуществ, но возможность максимального использования ресурсов процессора благодаря конкурентному поведению — это его главный плюс. Ниже мы рассмотрим основные принципы функционального программирования.
Вступление: Все эти принципы не обязательны (многие языки следуют им не полностью). Все они теоретические и нужны для наиболее точного определения функциональной парадигмы.
1. Все функции — чистые
Это правило безусловно является основным в функциональном программировании. Все функции являются чистыми, если они удовлетворяют двум условиям:
Второе правило — никаких побочных эффектов — является более широким по своей природе. Побочный эффект — это изменение чего-то отличного от функции, которая исполняется в текущий момент. Изменение переменной вне функции, вывод в консоль, вызов исключения, чтение данных из файла — всё это примеры побочных эффектов, которые лишают функцию чистоты. Может показаться, что это серьёзное ограничение, но подумайте ещё раз. Если вы уверены, что вызов функции не изменит ничего «снаружи», то вы можете использовать эту функцию в любом сценарии. Это открывает дорогу конкурентному программированию и многопоточным приложениям.
2. Все функции — первого класса и высшего порядка
Эта концепция — не особенность ФП (она используется в Javascript, PHP и других языках) — но его обязательное требование. На самом деле, на Википедии есть целая статья, посвящённая функциям первого класса. Для того, чтобы функция была первоклассной, у неё должна быть возможность быть объявленной в виде переменной. Это позволяет управлять функцией как обычным типом данных и в то же время исполнять её.
Функции высшего порядка же определяются как функции, принимающие другую функцию как аргумент или возвращающие функцию. Типичными примерами таких функций являются map и filter.
3. Переменные неизменяемы
Тут всё просто. В функциональном программировании вы не можете изменить переменную после её инициализации. Вы можете создавать новые, но не можете изменять существующие — и благодаря этому вы можете быть уверены, что никакая переменная не изменится.
4. Относительная прозрачность функций
Сложно дать корректное определение относительной прозрачности. Самым точным я считаю такое: если вы можете заменить вызов функции на возвращаемое значение, и состояние при этом не изменится, то функция относительно прозрачна. Это, быть может, очевидно, но я приведу пример.
Пусть у нас есть Java-функция, которая складывает 3 и 5:
Очевидно, что любой вызов этой функции можно заменить на 8 — значит, функция относительно прозрачна. Вот пример непрозрачной функции:
Эта функция ничего не возвращает, но печатает текст, и при замене вызова функции на ничто состояние консоли будет другим — значит, функция не является относительно прозрачной.
5. Функциональное программирование основано на лямбда-исчислении
Функциональное программирование сильно опирается на математическую систему, называющуюся лямбда-исчислением. Я не математик, поэтому я не буду углубляться в детали — но я хочу обратить внимание на два ключевых принципа лямбда-исчисления, которые формируют самое понятие функционального программирования:
Как я уже говорил, лямбда-исчисление на этом не заканчивается — но мы рассмотрели лишь ключевые аспекты, связанные с ФП. Теперь, в разговоре о функциональном программировании вы сможете блеснуть словечком «лямбда-исчисление», и все подумают, что вы шарите 🙂
Заключение
Функциональное программирование серьёзно напрягает мозги — но это очень мощный подход, и я считаю, что его популярность будет только расти.
Если вы хотите узнать о функциональном программировании побольше, то советуем вам ознакомиться с примерами использования принципов ФП в JavaScript (часть 1, часть 2), а также с циклом статей, посвящённым функциональному C#.
Что такое функциональное программирование
В программировании есть два больших подхода — императивное и функциональное. Они существенно отличаются логикой работы, ещё и создают путаницу в названиях. Сейчас объясним.
🤔 Функциональное — это про функции?
❌ Нет. Функциональное — это не про функции. Функции есть почти в любых языках программирования: и в функциональных, и в императивных. Отличие функционального программирования от императивного — в общем подходе.
Метафора: инструкция или книга правил
Представьте, что вы открываете кафе-столовую. Сейчас у вас там два типа сотрудников: повара и администраторы.
Для поваров вы пишете чёткие пошаговые инструкции для каждого блюда. Например:
Повар должен следовать этим инструкциям ровно в той последовательности, в которой вы их написали. Нельзя сначала почистить свёклу, а потом взять её. Нельзя посолить кастрюлю, в которой нет воды. Порядок действий важен и определяется вами. Это пример императивного программирования. Вы повелеваете исполнителем. Можно сказать, что исполнители выполняют ваши задания.
Для администратора вы пишете не инструкцию, а как бы книгу правил:
Это тоже команды, но исполнять их администратор будет не в этой последовательности, а в любой на своё усмотрение. Можно сказать, что задача этого человека — исполнять функции администратора, и мы описали правила, по которым эти функции исполнять. Это пример функционального программирования.
❌ Программисты, не бомбите
Конечно же, это упрощено для понимания. Вы сами попробуйте это нормально объяснить (можно прямо в комментах).
Императивное программирование
Примеры языков: C, С++, Go, Pascal, Java, Python, Ruby
Императивное программирование устроено так:
В языке есть команды, которые этот язык может выполнять. Эти команды можно собрать в подпрограммы, чтобы автоматизировать некоторые однотипные вычисления. В каком порядке записаны команды внутри подпрограммы, в том же порядке они и будут выполняться.
Есть переменные, которые могут хранить данные и изменяться во время работы программы. Переменная — это ячейка для данных. Мы можем создать переменную нужного нам типа, положить туда какое-то значение, а потом поменять его на другое.
Если подпрограмме на вход подать какое-то значение, то результат будет зависеть не только от исходных данных, но и от других переменных. Например, у нас есть функция, которая возвращает размер скидки при покупке в онлайн-магазине. Мы добавляем в корзину товар стоимостью 1000 ₽, а функция должна нам вернуть размер получившейся скидки. Но если скидка зависит от дня недели, то функция сначала проверит, какой сегодня день, потом посмотрит по таблице, какая сегодня скидка.
Получается, что в разные дни функция получает на вход 1000 ₽, но возвращает разные значения — так работает императивное программирование, когда всё зависит от других переменных.
Последовательность выполнения подпрограмм регулируется программистом. Он задаёт нужные условия, по которым движется программа. Вся логика полностью продумывается программистом — как он скажет, так и будет. Это значит, что разработчик может точно предсказать, в какой момент какой кусок кода выполнится — код получается предсказуемым, с понятной логикой работы.
Если у нас код, который считает скидку, должен вызываться только при финальном оформлении заказа, то он выполнится именно в этот момент. Он не посчитает скидку заранее и не пропустит момент оформления.
👉 Суть императивного программирования в том, что программист описывает чёткие шаги, которые должны привести код к нужной цели.
Звучит логично, и большинство программистов привыкли именно к такому поведению кода. Но функциональное программирование работает совершенно иначе.
Функциональное программирование
Примеры языков: Haskell, Lisp, Erlang, Clojure, F#
Смысл функционального программирования в том, что мы задаём не последовательность нужных нам команд, а описываем взаимодействие между ними и подпрограммами. Это похоже на то, как работают объекты в объектно-ориентированном программировании, только здесь это реализуется на уровне всей программы.
Например, в ООП нужно задать объекты и правила их взаимодействия между собой, но также можно и написать просто код, который не привязан к объектам. Он как бы стоит в стороне и влияет на работу программы в целом — отправляет одни объекты взаимодействовать с другими, обрабатывает какие-то результаты и так далее.
Функциональное программирование здесь идёт ещё дальше. В нём весь код — это правила работы с данными. Вы просто задаёте нужные правила, а код сам разбирается, как их применять.
Если мы сравним принципы функционального подхода с императивным, то единственное, что совпадёт, — и там, и там есть команды, которые язык может выполнять. Всё остальное — разное.
Команды можно собирать в подпрограммы, но их последовательность не имеет значения. Нет разницы, в каком порядке вы напишете подпрограммы — это же просто правила, а правила применяются тогда, когда нужно, а не когда про них сказали.
Переменных нет. Вернее, они есть, но не в том виде, к которому мы привыкли. В функциональном языке мы можем объявить переменную только один раз, и после этого значение переменной измениться не может. Это как константы — записали и всё, теперь можно только прочитать. Сами же промежуточные результаты хранятся в функциях — обратившись к нужной, вы всегда получите искомый результат.
Функции всегда возвращают одно и то же значение, если на вход поступают одни и те же данные. Если в прошлом примере мы отдавали в функцию сумму в 1000 ₽, а на выходе получали скидку в зависимости от дня недели, то в функциональном программировании если функция получит в качестве параметра 1000 ₽, то она всегда вернёт одну и ту же скидку независимо от других переменных.
Можно провести аналогию с математикой и синусами: синус 90 градусов всегда равен единице, в какой бы момент мы его ни посчитали или какие бы углы у нас ещё ни были в задаче. То же самое и здесь — всё предсказуемо и зависит только от входных параметров.
Последовательность выполнения подпрограмм определяет сам код и компилятор, а не программист. Каждая команда — это какое-то правило, поэтому нет разницы, когда мы запишем это правило, в начале или в конце кода. Главное, чтобы у нас это правило было, а компилятор сам разберётся, в какой момент его применять.
В русском языке всё работает точно так же: есть правила правописания и грамматики. Нам неважно, в каком порядке мы их изучили, главное — чтобы мы их вовремя применяли при написании текста или в устной речи. Например, мы можем сначала пройти правило «жи-ши», а потом правило про «не с глаголами», но применять мы их будем в том порядке, какой требуется в тексте.
👉 Получается, что смысл функционального программирования в том, чтобы описать не сами чёткие шаги к цели, а правила, по которым компилятор сам должен дойти до нужного результата.
5 функциональных языков программирования, которые вы должны знать
Если вы проводите какое-то время, читая о тенденциях программирования в интернете, вы наверняка услышите о функциональном программировании. Термин встречается довольно часто, но что он означает?
Даже если вы знаете, что такое функциональное программирование, вам может быть неясно, какие языки лучше всего подходят для него. Ведь не все языки программирования одинаковы. Хотя вы можете применять парадигмы функционального программирования на многих языках, есть еще некоторые, используя которые, вы будете чувствовать себя гораздо более комфортно.
Что Такое Функциональное Программирование?
Если вы хорошо знаете и любите, у вас есть преимущество в функциональном программировании. Это связано с тем, что парадигма функционального программирования рассматривает вычисления как математические функции.
В основном, функциональное программирование рассматривает функции и данные как неизменяемые. Вы передаете данные в функцию, и она обычно возвращает эти данные, преобразованные в какой-то другой тип данных. В функциональном программировании функция никогда не должна изменять исходные данные или состояние программы.
Есть сходство с философией Unix, что каждая программа должна делать одну вещь хорошо. Функция не должна касаться различных частей программы. Вместо этого она должна принимать данные на входа и давать значение на выходе.
В идеале, функции должны быть чистыми, когда это возможно в функциональном программировании. Это означает, что при одних и тех же входных данных выходные данные функции всегда будут оставаться одними и теми же.
Функциональное и объектно-ориентированное программирование
Это драматический отход от чего-то вроде объектно-ориентированного программирования. В объектно-ориентированном программировании часто имеется базовый объект с различными методами, предназначенными для изменения данных или состояния, которые являются частью этого объекта. Метод может даже изменять данные или состояние, если не указан явно.
В практических программах иногда это имеет смысл. Тем не менее, это может усложнить поддержку программы, так как не всегда ясно, что изменяет состояние или данные. Функциональное программирование первоначально использовалось в академических целях, но также может помочь решить множество задач.
1. Javascript
Некоторые языки программирования позволяют выполнять все функции функционального программирования, в то время как другие либо не сильно годятся для этого. JavaScript относится к первой категории. В то же время вы можете так же легко использовать объектно-ориентированный подход.
Тем не менее, есть много функциональных парадигм программирования, встроенных в JavaScript. Возьмем, например, функции более высокого порядка. Это функции, которые могут принимать другие функции в качестве аргументов.
2. Python
Как и JavaScript, Python является обобщенным языком, с помощью которого можно использовать любое количество парадигм программирования. Python может иметь свои недостатки, но функциональное программирование не является одним из них. Существует даже введение в функциональное программирование в официальной документации Python.
Лямбда-выражения можно использовать несколькими способами. Один из способов использовать его в качестве стенографии для простых функций. При назначении переменной можно вызывать лямбда-выражения точно так же, как и стандартную функцию Python. Реальное преимущество лямбда-выражений заключается в использовании их в качестве анонимных функций.
Анонимные функции работают на JavaScript и других языках из этого списка. Они особенно удобны при использовании с функциями более высокого порядка, так как вы можете определить их сразу. Без анонимных функций вам пришлось бы заранее определять даже простые добавления как специальные функции.
3. Clojure
В отличие от JavaScript и Python, Clojure может быть не совсем знакомым языком, даже среди программистов. В случае, если вы не знакомы с Clojure – этот язык является диалектом языка программирования Lisp, который придумали к концу 1950-х годов.
Как и другие диалекты Lisp, Clojure рассматривает код как данные. Это означает, что код может эффективно изменять себя. В отличие от других диалектов Lisp, Clojure работает на платформе Java и компилируется в байт-код JVM. Это означает, что он может работать с библиотеками Java, были ли они написаны на Clojure или нет.
В отличие от предыдущих языков в этом списке, Clojure изначально является функциональным языком программирования. Это означает, что он защищает неизменность везде, где это возможно, особенно в рамках структур данных.
4. Elm
Один из новых языков в этом списке, Elm-чисто функциональный язык, первоначально разработанный Evan Czaplicki в 2012 году. Язык приобрел популярность среди веб-разработчиков, в частности, для создания пользовательских интерфейсов.
В отличие от всех предыдущих в этом списке, Elm использует статическую проверку типов. Это помогает гарантировать отсутствие исключений во время выполнения, когда ошибки перехватываются во время компиляции. Это означает? что будет меньше видимых ошибок для пользователей, что является большим плюсом.
Компилятор Elm предназначен для HTML, CSS и JavaScript. Так же, как вы можете использовать Clojure для написания программ, которые работают на Java, вы можете писать приложения, которые используют библиотеки JavaScript в Elm.
5. Haskell
Haskell – это другой статически типизированный, чисто функциональный язык. В отличие от Elm, Haskell существует уже довольно долго. Первая версия языка была разработана в 1990 году. Последний стандарт-Haskell 2010, а следующая версия запланирована на 2020 год.
Как мы уже знаем, чисто функциональная природа Haskell означает, что по замыслу, функции не должны иметь побочных эффектов. Это делает его хорошо подходящим для решения реальных проблем, несмотря на корни функционального программирования в академических кругах.
Несмотря на отсутствие популярности, Haskell был использован в некоторых широко используемых проектах. Оконный менеджер Xmonad полностью написан на языке Haskell. Pandoc, который преобразует различные типы разметки в другие форматы..
Вы новичок в программировании?
Некоторые из вышеперечисленных терминов и языков могут показаться несколько эзотерическими, если вы еще не опытный кодер. Это хорошо, так как знание того, чего вы не знаете, является одним из первых шагов в становлении хорошего специалиста.
Современные языки программирования, которые заставят вас страдать: Часть 2, функциональные языки
Современные языки программирования, которые заставят вас страдать: Часть 2, функциональные языки
Прим. ред. Это перевод статьи Ильи Суздальницкого. Мнение редакции может не совпадать с мнением автора оригинала.
Это вторая и финальная часть перевода статьи про современные язки программирования. В первой части — «Современные языки программирования, которые заставят вас страдать: Часть 1, ООП», рассматривались объектноориентированные языки. В этой части автор подробно разбирает функциональные языки программирования которые принадлежат семейству ML ( и некоторые С-подобные).
Функциональные языки программирования
Haskell
Типизация: нет более мощной системы типов, чем в Haskell. Он поддерживает, как алгебраические типы данных, так и классы типов. Язык способен вывести почти любой тип.
Сложность изучения: чтобы продуктивно использовать Haskell нужно изучить теорию категорий. Даже для того, чтобы написать hello world, нужно понимать монады.
Сообщество: больше заинтересовано в академических дискуссиях, чем в решении реальных проблем.
Функциональная чистота: чистые функции — прекрасны. Побочные эффекты (взаимодействие с внешним миром, изменение состояния) причина большого количества ошибок в программах. Будучи чисто функциональным языком, Haskell полностью избавлен от них.
Конечно в языке есть обходные пути, для взаимодействия с внешним миром. Для этого используется набор инструкций (монад ввода-вывода). Это могут быть такие инструкции: получить строку с клавиатуры, использовать её в некой функции, напечатать результат в консоль. Среда выполнения делает это за нас. Мы никогда не выполняем код взаимодействующий с внешним миром напрямую.
На практике, такая сосредоточенность на функциональной чистоте значительно увеличивает число абстракций, усложняет код и уменьшает продуктивность разработчиков.
Поддержка NULL: как и в Rust, Haskell не поддерживает нулевые ссылки. Вместо этого в нём есть Optional, на случай, если значения может не быть.
Обработка ошибок: некоторые функции могут выбрасывать ошибки, но более свойственный для языка подход похож на Result в Rust.
Иммутабельность: язык имеет первоклассную поддержку иммутабельных структур данных.
Сопоставление с образцом: поддерживается.
Экосистема: стандартная библиотека неорганизованна. По умолчанию, в Haskell используются функции выбрасывающие исключение, вместо возврата Option (золотой стандарт для функционального программирования). В довершение всего, у Haskell есть два менеджера пакетов — Cabal и Stack.
Вердикт: мне бы очень хотелось полюбить Haskell, однако он навсегда застрял в академических кругах. Является ли он худшим из функциональных языков? Решать вам, но я думаю, что это так.
OCaml
Типизация: не поддерживает классы типов, но есть функторы (модули высшего порядка). Язык статически типизирован и выводит типы почти также хорошо, как Haskell.
Экосистема: имеет небольшое сообщество и страдает от недостатка библиотек. Языку не хватает достойного веб-фреймворка. Документация хуже чем других языков.
Инструментарий: инструменты языка неорганизованны. Имеет три менеджера пакетов: Opam, Dune, и Esy. Язык известен некачественными сообщениями об ошибках компилятора. Это не критично, но снижает производительность программистов.
Параллелизм: разработчики годами ждут поддержки многоядерности, но её пока что не предвидится.
Поддержка NULL: нет нулевых ссылок, использует Option для неуказанных значений.
Обработка ошибок: нативный подход — использование типа Result.
Иммутабельность: язык имеет первоклассную поддержку иммутабельных структур данных.
Сопоставление с образцом: поддерживается.
Вердикт: Ocaml — хороший функциональный язык. Его основные недостатки: отсутствие поддержки многоядерности и небольшое сообщество (причина недостатка обучающих материалов и библиотек). Поэтому, я бы не рекомендовал язык к использованию в работе.
Scala
Экосистема: Scala — это язык из семейства Си, который выполняется на виртуальной машине Java. Это значит, что у вас есть доступ к огромной экосистеме библиотек Java.
Типизация: язык плохо справляется с приведением типов. Однако Scala поддерживает Higher-Kinded типы и типы классов.
Немногословность/читаемость: хотя программы на Scala и отличаются лаконичностью (особенно по сравнению с Java), читаемость страдает. Scala — один из немногих функциональных языков, принадлежащих к семейству Си. Си-подобные языки были предназначены для императивного программирования, а ML для функционального. Поэтому функциональный код на Scala может иногда выглядеть странно.
Синтаксис для алгебраических типов данных оставляет желать лучшего:
Этот же код на языке ReasonML:
Скорость: hello world на языке Scala может компилироваться до 10 секунд, на слабом железе. Компиляция производится только на одном ядре процессора, что отрицательно влияет на скорость.
Из-за того, что Scala работает на виртуальной машине Java, программы запускаются дольше.
Сложность изучения: один из самых сложных функциональных языков. Scala, как и C++ обладает множеством функций, которые, однако, усложняют его изучение.
Иммутабельность: Scala обладает первоклассной поддержкой неизменяемых структур данных (с использованием классов образцов).
Поддержка NULL: с одной стороны, Scala поддерживает нулевые ссылки. С другой стороны, характерный для языка способ обработки отсутствующих значений — паттерн Option.
Обработка ошибок: нативный подход — использование типа Result.
Параллелизм: можно использовать отличный инструмент — Akka.
Сопоставление с образцом: поддерживается.
Вердикт: Scala пытается делать слишком многое. Его разработчикам пришлось пойти на множество компромиссов, чтобы поддерживать как ООП, так и функциональное программирование.
Сообщения об ошибках: компилятор выдаёт самые понятные сообщения об ошибках, что я когда-либо видел.
Обработка ошибок: в языке нет ошибок выполнения и исключений. Как и другие функциональные языки, использует тип Result для обработки ошибок.
Функциональная чистота: как и Haskell, Elm — чисто функциональный язык. И в данном случае это скорее минус, потому что любой рефакторинг превращается в кошмар.
Слишком строгий:
Скриншот с сайта https://www.reddit.com/r/ProgrammerHumor/comments/8we9zh/im_learning_elm_and_it_immediately_declared_war/
Elm настолько строгий, что использование табуляций считается синтаксической ошибкой.
Сосредоточенность на отсутствии ошибок убивает язык. В версии 0.19, взаимодействие с JS библиотеками сделали практически невозможным. Конечно для того, чтобы стимулировать людей писать свои библиотеки на Elm. Но компаний, у которых есть для этого достаточно ресурсов, крайне мало.
Поддержка React: Elm создаёт свою собственную виртуальную модель DOM и не использует React. Это лишает разработчиков доступа к обширной экосистеме библиотек и компонентов, созданных для React.
Состояние языка: с каждым новым релизом в языке происходят сильные изменения, которые могут лишить вас возможности использовать его.
К сожалению, прошло уже больше года с тех пор, как была выпущена новая версия Elm (0.19.1). О состоянии разработки ничего не известно. Возможно, что она вообще больше не ведётся.
Сопоставление с образцом: поддерживается.
Иммутабельность: обладает первоклассной поддержкой неизменяемых структур данных.
Поддержка NULL: нет нулевых ссылок, использует Option для неуказанных значений.
Вердикт: Elm — отличный язык, но к сожалению у него нет будущего.
Типизация: единственный минус его системы типов — отсутствие Higher-Kinded типов. Тем не менее система типов очень надежна, компилятор способен вывести практически все что угодно. F# имеет надлежащую поддержку алгебраических типов данных.
Не полностью функциональный: в отличие от Haskell/Elm, F# очень прагматичен и не обеспечивает функциональную чистоту.
Обучающие ресурсы: есть действительно хорошие учебные ресурсы.
Сложность изучения: F# — один из самых простых функциональных языков.
Экосистема: имеет довольно небольшое сообщество и в отличие от таких языков как Elixir, оно не имеет таких же замечательных библиотек.
Параллелизм: работает поверх CLR, который не имеет такой же превосходной поддержки параллелизма, как Elixir на виртуальной машине Erlang.
Поддержка NULL: NULL-значения обычно не используются. Неуказанные значения обрабатываются с помощью паттерна Option.
Обработка ошибок: ошибки обрабатываются с помощью паттерна Result.
Иммутабельность: обладает первоклассной поддержкой неизменяемых структур данных.
Сопоставление с образцом: поддерживается.
Вердикт: F# — очень надежный язык программирования с действительно хорошей системой типов. Он почти так же хорош, как Elixir для разработки Web API (подробнее об этом далее). Однако проблема F# заключается не в том, что у него есть, а в том, чего у него нет. Если сравнить его с Elixir, его функционал параллелизма, богатая экосистема и удивительное сообщество перевешивают любые преимущества статической типизации, которые предоставляет F#.
Однако F# — лучший язык для финтеха. Также язык отлично подойдёт для энтерпрайз разработки. Его мощная система типов позволяет моделировать сложную бизнес логику. Очень рекомендую прочитать эту книгу — «Domain Modeling Made Functional».
ReasonML
Не является надмножеством JavaScript: синтаксис ReasonML похож на JavaScript, что делает его более доступным для всех, кто имеет опыт работы с JavaScript. Однако, в отличие от TypeScript, ReasonML даже не пытается быть надмножеством JavaScript. ReasonML не должен был унаследовать плохие дизайнерские решения, десятилетиями принимаемые в JavaScript.
Сложность изучения: ReasonML является одним из самых простых функциональных языков.
Не полностью функциональный: ReasonML очень прагматичен, ориентирован на производительность разработчиков и быстрое достижение результатов.
Типизация: его система типов почти так же хороша, как у Haskell. Самым большим недостатком является отсутствие классов типов, но он поддерживает функторы (модули высшего порядка).
ReasonML статически типизирован и выводит типы почти так же хорошо, как и Haskell.
Экосистема: как и TypeScript, ReasonML имеет доступ ко всей экосистеме JavaScript.
Взаимодействие с JavaScript/TypeScript: компилируется в обычный JavaScript. Поэтому, в одном проекте можно использовать как ReasonML, так и JavaScript/TypeScript.
ReasonML and React — отличное сочетание: поскольку ReasonML статически типизирован, нет необходимости беспокоиться о PropTypes. В отличие от JavaScript, при использовании ReasonML ничто не перерисовывается без необходимости — вы получаете отличную производительность React из коробки!
Инструменты: язык далеко не такой зрелый, как его альтернативы, так что могут возникнуть некоторые проблемы с инструментами. Например, официально рекомендуемое расширение VSCode — reason-language-server в настоящее время не работает.
ReasonML использует компилятор OCaml под капотом, а OCaml известен посредственными сообщениями об ошибках компилятора. Это не критично, но может повлиять на производительность разработчиков.
Поддержка NULL: нет нулевых ссылок, использует Option для неуказанных значений.
Иммутабельность: обладает первоклассной поддержкой неизменяемых структур данных.
Сопоставление с образцом: поддерживается.
Вердикт: это отличный язык для веб-разработки. ReasonML, вероятно, является тем, чем всегда стремился быть TypeScript, но потерпел неудачу. ReasonML добавляет статическую типизацию в JavaScript, убирая при этом все плохие фичи (и добавляя современные фичи, которые действительно нужны).
Elixir
Экосистема: это сильная сторона языка. Автор языка также разрабатывает крутые библиотеки: Phoenix и Ecto. В отличие от других языков, у Elixir нет множества библиотек с дублирующимся функционалом, а существующие — очень хороши.
Имеет хорошую документацию, даже к стандартной библиотеке.
Фреймворк Phoenix: поддерживает из коробки: вебсокеты, routing, HTML templating language, internationalization, JSON encoders/decoders, seamless ORM integration(Ecto), sessions, SPA toolkit и многое другое. Также фреймворк известен своей производительностью — способен обрабатывать миллионы одновременных подключений на одной машине.
Фуллстек Elixir: Phoenix недавно представил LiveView, который позволяет создавать насыщенные веб-интерфейсы реального времени прямо в Elixir. LiveView даже заботится о синхронизации состояния клиента и сервера, а это значит, что нам не нужно беспокоиться о разработке и обслуживании REST/GraphQL API.
Обработка данных: Elixir может быть надежной альтернативой Python для многих задач связанных с обработкой данных. Скрепер есть как у Python, так и у Elixir, и последний предлагает гораздо лучшее решение и экосистему для этой задачи.
Такие инструменты, как Broadway, позволяют строить конвейеры приема/обработки данных в Elixir.
Типизация: на мой взгляд, отсутствие корректной статической типизации — самый большой недостаток Elixir.
Скорость: компилятор Elixir является многопоточным и обеспечивает невероятно высокую скорость компиляции. В отличие от JVM, виртуальная машина Erlang запускается быстро. Производительность во время выполнения очень хороша.
Надёжность: код на Elixir выполняется поверх Erlang, который использовался более 30 лет для создания самого надежного программного обеспечения в мире. Некоторые программы, работающие на виртуальной машине Erlang, смогли достичь надежности 99,9999999%. Ни одна другая платформа в мире не может похвастаться таким же уровнем надежности.
Параллелизм: язык использует альтернативный подход к параллелизму — модель акторов. При таком подходе, у акторов (процессов) нет ничего общего. Единственный способ общения между различными процессами — отправка сообщений.
Elixir, в отличие от Go, убивает только тот процесс в котором произошла ошибка, а не всю программу. Более того, этот процесс будет автоматически перезапущен его супервизором.
Elixir строится на основе Erlang, который известен своими большими возможностями распараллеливания, и использует совершенно другой подход к параллелизму, называемый моделью актора. В рамках этой модели, между процессами (акторами) нет ничего общего. Каждый процесс поддерживает свое собственное внутреннее состояние, и единственный способ общения между различными процессами — отправка сообщений.
Процессы в Elixir очень легковесны, их можно запускать тысячами на одной машине.
Масштабирование: параллельные вычисления в Go быстрее чем в Elixir, если это происходит на одной машине. Но при масштабировании происходит обратное. Elixir легко справляется с такими вещами как: кластеризация, RPC и сетевые взаимодействия. В некотором смысле, виртуальная машина Erlang работала с микросервисами за десятилетия до того, как они вошли в обиход. Каждый процесс можно рассматривать как микросервис — как и микросервисы, процессы независимы друг от друга. Микросервисы без сложностей Kubernetes? Именно для этого и был создан Elixir.
Обработка ошибок: язык использует уникальный подход к обработке ошибок. В то время как чисто функциональные языки (Haskell/Elm) предназначены для минимизации вероятности появления ошибок, Elixir предполагает, что ошибки неизбежно произойдут.
Выбрасывать исключения в Elixir — правильно, в то время как обрабатывать исключения обычно не рекомендуется. Вместо этого супервизор процесса автоматически перезапустит неудачный процесс, чтобы программа продолжила работать.
Сложность изучения: язык можно освоить за пару месяцев. Однако освоение OTP может занять некоторое время. OTP — киллер фича языка. OTP — это набор инструментов и библиотек от Erlang, на которых строится Elixir. Это секретный ингредиент, который значительно упрощает построение параллельных и распределенных программ.
Обучающие ресурсы: их существует огромное количество. И почти все из них подойдут для новичков.
Сопоставление с образцом: поддерживается.
Вычисления: язык не справляется с задачами в которых требуется большое количество вычислений.
Вердикт: Elixir, вероятно, является самым зрелым из всех функциональных языков. Он работает на виртуальной машине, созданной для функционального программирования. Язык был разработан с нуля для параллельных вычислений, и идеально подходит для современной эры многоядерных процессоров. Это лучший язык для Web API. OTP и модель акторов делают язык лучшим решением для параллельных и распределённых программ.
Подходящий инструмент
Точно также, как не стоит пытаться забить гвоздь отвёрткой, не нужно использовать один язык программирования повсюду. Каждый из них имеет свою область применения.
Go — лучший язык для системного программирования. Для фронтенда несомненно стоит выбрать ReasonML. Абсолютный лидер для разработки Web API — Elixir. Как и для любых задач связанных с параллельными и распределёнными программами. Python, это к сожалению единственный адекватный вариант для data science.
Это довольно неоднозначная статья. Очевидно, что автор предпочитает функциональные языки программирования объектноориентированным. Если вы не согласны с рейтингом и можете аргументировать свою точку зрения, добро пожаловать в комментарии.