В предыдущем посте о функциональном программировании мы видели некоторые преимущества, которые дает нам эта парадигма программирования, мы также могли видеть некоторые преимущества по сравнению с объектно-ориентированным программированием, на этот раз мы немного узнаем о фокусе функций в рамках функционального программирования через первый функции класса и чистые функции. Если вам нравится этот контент, вы можете поставить мне лайк или оставить комментарий.

Функции первого класса

В функциональном программировании функции являются гражданами первого класса, что означает, что с ними можно обращаться как со строками, логическими значениями, числами и т. д. Таким образом, функции могут быть переданы в качестве параметров другим функциям, сохранены в массивах, в переменных, во всем, что вы делаете с обычными данными.

Однако очень часто можно найти очень плохие примеры функций первого класса, например следующие:

const print = param => console.log(param)

В этом случае совершенно не нужно создавать еще одну функцию, которая принимает тот же параметр и выполняет то же действие, это было бы проще сделать следующим образом:

const print = console.log

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

Очень важно понять это, прежде чем продолжить, поэтому важно просмотреть несколько примеров, чтобы убедиться, что мы поняли:

// ignorant
const getData = callback => ajax(callback)
// enlightened
const getData = ajax;

Давайте посмотрим на другой пример, который позволит нам легче проверить, почему я так настойчив:

const ProductController = {
 index(product) { return Views.index(product); },
 show(product) { return Views.show(product); },
 create(attrs) { return Db.create(attrs); },
 update(product, attrs) { return Db.update(product, attrs); },
 destroy(product) { return Db.destroy(product); },
};

Практически мы могли бы полностью переписать этот контроллер как:

const ProductController = {
 index: Views.index
 show: Views.show
 create: Db.create
 update: Db.update
 delete: Db.delete
};

Важность первоклассных функций

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

httpGet(‘/product/1232’, (json,error) => renderProduct(json,error));

Было бы намного легче читать, если бы мы сделали это так:

httpGet(‘/product/1232’, renderProduct);

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

// specific to our current product
const validProduct = products =>
  products.filter(product => product !== null && product !== undefined);
// vastly more relevant for future projects
const general = xs => xs.filter(x => x !== null && x !== undefined);

Чистые функции в глубине

Чистые функции никогда не должны изменять какое-либо значение за пределами своей области, поэтому они должны всегда возвращать одно и то же значение для данного ввода, ярким примером этого может быть метод среза, который, в отличие от сплайсинга, всегда возвращает один и тот же результат для одних и тех же параметров.

const arr = [1,2,3,4,5];
// pure
console.log(arr.slice(0,3)); // [1,2,3]
console.log(arr.slice(0,3)); // [1,2,3]
console.log(arr.slice(0,3)); // [1,2,3]
// impure
console.log(arr.splice(0,3)); // [1,2,3]
console.log(arr.splice(0,3)); // [4,5]
console.log(arr.splice(0,3)); // []

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

// impure
let minAge = 18;
const isAdult = age => age >= minAge;
// pure
const isAdult = (age) => {
    const minAge = 18;
    return age >= minAge;
};

Здесь мы видим, что в чистой форме написания нашей функции мы устраняем зависимость глобального состояния, а также устанавливаем константу для переменной minAge, чтобы избежать изменения ее значения.

Побочные эффекты

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

Математические функции

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

В следующем примере показана допустимая функция:

В следующем примере показана недопустимая функция:

Преимущества чистых функций

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

const memoize = (f) => {
    const cache = {};
    return (...args) => {
        const argStr = JSON.stringify(args);
        cache[argStr] = cache[argStr] || f(...args);
        return cache[argStr];
    };
};
const square = memoize(x => x * x);
square(4); // 16
square(4); // 16, returns cache for input 4
square(5); // 25
square(5); // 25, returns cache for input 5

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

// impure
const list=[1,2,3,4]
const printList = () => {
    list.forEach(e=>console.log(e))
};
// pure
const printList = (list) => {
   list.forEach(e=>console.log(e))
};

Тестируемость: чистые функции очень легко тестировать, что помогает нам значительно сократить время тестирования нашего приложения.

На сегодня это все в следующем посте Посмотрим о карри и сочинении. Увидимся завтра.

Спасибо за чтение, следуйте за мной, чтобы узнать больше