Парадигма функционального программирования

Принципы функционального программирования:

  1. Масштабирование функции. Означает, что одну и ту же функцию можно запустить на нескольких серверах и потоков паралельно
  2. Отсутствие состояния. Означает, что функция хранит в себе данных, которые меняют со временем, например сессии, не хранит изменяемых локально файлов. Хранение состояний происхиодт во внешних системах, таких как база данных, облачные файловые хранилища, менеджеры сессий, и т.п.
  3. Кооперативная многозадачность. Это принцип, по которому один поток, может выполнять паралельно несколько функций. Выполнение другой функции происходит, когда другая функция ждет ответа от внешней системы.
  4. Неизменяемые структуры данных. Означает, что функция не меняет входные данные и возвращаем неизменяемый тип.
  5. Чистые функции:
    • Всегда возвращает одинаковый результат, на одни и те же входные данные
    • Не изменяет входные данные
    • Не обращается к внешним системам: базе, диску, глобальным переменным и прочим внешним источниками данных
    • Не является асинхронной
    • Результат выполнения чистых функций можно кэшировать
  6. Карирование и функции высших порядков.Функция высшего порядка – это функция, которая возвращает другую функцию. А карирование, это метод создания функций высшего порядка.
  7. Pipe. Это такой способ написание вызова функций, когда результат предыдущей попадает во вход следущей

Пример кода

Пусть определен класс, с тремя функциями:

  • Lib::sum - вычисляет сумму двух чисел
  • Lib::isInstance - карировання функция, проверяет item на class_name
  • Lib::equal - карировання функция, проверяет item с value

namespace Test;

class Lib
{
	
	/**
	 * Sum a + b
	 */
	lambda int sum(int a, int b) => a + b;
	
	
	/**
	 * Check object is istance
	 */
	lambda fn isInstance(string class_name) =>
		bool (var item) use (class_name) => rtl::is_instanceof(item, class_name)
	;
	
	
	/**
	 * Equal two value by key
	 */
	lambda fn equal(var value)
		=> bool (var item) use (value)
			=> item == value
	;
	
	
	/**
	 * Equal two struct by key
	 */
	lambda fn equalNot(var value)
		=> bool (var item) use (value)
			=> item != value
	;
	
}

Примеры

Collection names = ["1", "2", "3", "2"];

/* Оставим цифры, не равные 2 */
Collection names2 = names.filter( Lib::equalNot("2") );

/* Найдем цифру 3 в массиве */
int pos = names.find( Lib::equal("2") );

Найдем определенный класс в массиве

Collection classes = 
[
	new User{},
	new Test{},
	new Entity{},
	new Route{},
];

/* Получим пользователя из массива */
User user = (User) classes.find( Lib::isInstance(classof User) );

/* Получим маршрут из массива */
Route route = (Route) classes.find( Lib::isInstance(classof Route) );

Pipe

User user = new User()
	|> User::setName('John')
	|> User::addEmail("john@example.com")
;

Или проще:

User user = new User()
	-> setName('John') 
	-> addEmail("john@example.com")
;

В данном случае при вызове addEmail, первым аргументом передается измененный объект, а вторым строка "john@example.com". Pipe может быть асинхронным.