Сейчас самой распространенной парадигмой программирования в бизнес-разработке является объектно-ориентированное программирование (ООП), которое строится вокруг 4 ключевых принципов:
Парадигма ООП имеет множество преимуществ, но иногда не подходит под некоторые задачи
Так, например, в ООП у объекта есть внутреннее изменяемое состояние, от которого неявно зависит поведение функции. Например, в этом примере, функция изменяет переданный объект (а именно удаляет запись в нем):
class Validator {
static void validate(Map<String, User> users) {
if (users.containsKey("admin") && !users.get("admin").isActive()) {
users.remove("admin"); // валидация мутирует входные данные
}
}
}
Map<String, User> users = new HashMap<>();
users.put("admin", new User("Alice", false));
Validator.validate(users);
User admin = users.get("admin"); // admin == null
В функциональной парадигме у объектов иммутабельность по умолчанию, то есть данные объекта не изменяется, а вместо этого создается новый объект с другими данными
users = [("admin", User ("Alice", False))]
users' = validate users
result1 = lookup "admin" users' -- "admin" не найдется
result2 = lookup "admin" users -- найдется объект "Alice"
Также функции в ООП зачастую имеют побочные эффекты. Это значит, что результат их выполнения зависит не только от аргументов, но и от внутреннего состояния других объектов, например, базы данных:
double withdraw(BankAccount account, double amount) {
if (amount > account.getBalance()) {
logger.error("Not enough money for account {}", account.getId());
throw new IllegalStateException("Insufficient funds");
}
account.setBalance(account. getBalance() - amount);
logger.info("Withdraw {} from {}", amount, account.getId());
auditService. save(account);
return account.getBalance();
}
В функциональных языках все функции считаются чистыми, то есть их результат зависит только от входных параметров. Из этого следует 4 важных свойства:
Также чистые функции позволяют выполнять ленивые вычисления. Например, такой код на Java:
public int firstArgument(int a, int b) {
return a;
}
firstArgument(1, 1/0); // ArithmeticException
запустится в ошибкой ArithmeticException, так как сначала нужно вычислить значения параметров 1 и 1/0, а затем вызвать функцию, несмотря на то, что второй аргумент не применяется. В функциональном языке с ленивыми вычислениями второй аргумент не вычисляется, из-за чего программа выполняется:
firstArgument a b = a
result = firstArgument (1) (1/0) -- ok
Подытожим ключевые идеи функциональной парадигмы:
Сейчас самым распространенным функциональным языком программирования является Haskell, появившийся в конце 1980-ых
Язык Haskell имеет компилятор GHC (от Glasgow Haskell Compiler) ghc и интерактивный интерпретатор GHCi ghci. Интерпретатор (он же REPL, от Read-Eval-Print Loop) имеет свои команды:
:quit - выход из интерпретатора:load File.hs или :l File.hs - загрузка модуля в сессию:reload или :r - рекомпиляция модулей с измененным кодомВ качестве системы сборки и пакетных менеджеров применяются два варианта:
Cabal, появившийся в 2005. Основные команды:
cabal initcabal replcabal buildcabal runStack, который является оберткой над Cabal
stack new <название-проекта>stack ghcistack buildstack runРаньше Cabal имел проблему с зависимостями, из-за чего на диске сохранялась только одна версия конкретного пакета, в ответ на что был создан Stack. Но с приходом Cabal версии 2 проблема исчезла. Сейчас Cabal используется по умолчанию
Функциональные языки программирования хорошо подходят для создания компиляторов, парсинга и форматирования строк, CLI-утилит и бэкенда, но плохо подходят для машинного обучения, создания графического интерфейса и игровой разработки