Введение в функциональное программирование с TypeScript и JavaScript


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

С ростом сложности программ и их бесчисленных состояний мы возвращаемся к старой парадигме: функциональной. Многие программисты уже привыкли к объектно-ориентированной парадигме, поэтому я начну приводить сравнение. Объектно-ориентированное программирование имеет множество шаблонов и правил, но мы можем обобщить их следующим образом: Объектно-ориентированное программирование налагает дисциплину на косвенную передачу управления данными, и это по сравнению с функциональным программированием Функциональное программирование налагает дисциплину на присвоение. Эти две фразы многое говорят о соответствующих парадигмах, которые мы рассмотрим в этой статье блога.

Что это для меня?

Когда вы познаете новую парадигму, вы получаете оружие для решения старых проблем еще проще, чем раньше. Я покажу «чистый» скрипт на TypeScript, затем другой нечистый скрипт на TypeScript и, наконец, хороший пример того же упражнения на clojure.
Ниже я приведу весь код скрипта и давайте проанализируем и поймем, что происходило

Начало

const customFunction = (
  value1: number,
  value2: number,
  funcEvaluater: string,
  textObj: { todayText: string; tomorrowText: string }
) => {
  function createdFunction(funcEvaluater) {
    return eval(funcEvaluater);
  }

  const currier =
    (textObj: { todayText: string; tomorrowText: string }) =>
    (val2: string) =>
    (val3: string) => {
      return textObj.todayText + val2 + textObj.tomorrowText + val3;
    };

  return [value1 + value2, createdFunction(funcEvaluater), currier(textObj)];
};

const [sum, evaluator, curryWhater] = customFunction(2, 3, '3 + 3', {
  todayText: 'Today is: ',
  tomorrowText: ' and tomorrow will be: ',
});

const getTodayWheterApi = (userLocation: string) => {
  // call api
  return 'Raining';
};
const getTomorrowWheterApi = (userLocation: string) => {
  // call api
  return 'Sunny';
};
// Chama a localização do user aqui
const currentLocation = 'userCurrentLocation';
appDiv.innerHTML = `
<h2>value: ${sum}</h2>
<h3>Eval: ${evaluator} </h3>
<h3>curry: ${curryWhater(getTodayWheterApi(currentLocation))(getTomorrowWheterApi(currentLocation))} </h3>`;
Войдите в полноэкранный режим Выход из полноэкранного режима

Чистые функции

Единственный идеальный способ обертывания — это функции. Мы можем видеть в нашей функции под названием customFunction

const customFunction = (
  value1: number,
  value2: number,
  funcEvaluater: string,
  textObj: { todayText: string; tomorrowText: string }
) => {
  function createdFunction(funcEvaluater) {
    return eval(funcEvaluater);
  }

  const currier =
    (textObj: { todayText: string; tomorrowText: string }) =>
    (val2: string) =>
    (val3: string) => {
      return textObj.todayText + val2 + textObj.tomorrowText + val3;
    };

  return [value1 + value2, createdFunction(funcEvaluater), currier(textObj)];
};
Войдите в полноэкранный режим Выход из полноэкранного режима

Все, что вы используете в этой функции, вы получаете как параметр, и все равно есть возврат, так что вы заканчиваете работу, делаете какой-то расчет, вызов api или любую другую обработку в потоке данных и, наконец, возвращает то, что вы сделали, в этом случае мы получили 4 аргумента, 2 значения, которые будут добавлены и возвращены первыми, функция, которая будет оценена (не используйте eval в production) и, наконец, возвращается также помимо результата eval, карри на основе объекта, который был передан в качестве параметра, карри в нескольких словах — это функция, которая генерирует другую функцию.

Каков результат этого?

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

const [sum, evaluator, curryWhater] = customFunction(2, 3, '3 + 3', {
  todayText: 'Today is: ',
  tomorrowText: ' and tomorrow will be: ',
});
Войдите в полноэкранный режим Выход из полноэкранного режима

И результат в пользовательском интерфейсе будет выглядеть следующим образом

const getTodayWheterApi = (userLocation: string) => {
  // call api
  return 'Raining';
};
const getTomorrowWheterApi = (userLocation: string) => {
  // call api
  return 'Sunny';
};
// Chama a localização do user aqui
const currentLocation = 'userCurrentLocation';
html do user
appDiv.innerHTML = `
<h2>value: ${sum}</h2>
<h3>Eval: ${evaluator} </h3>
<h3>curry: ${curryWhater(getTodayWheterApi(currentLocation))(getTomorrowWheterApi(currentLocation))} </h3>`;
Войдите в полноэкранный режим Выход из полноэкранного режима

на приведенном ниже рисунке показан результат

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

И как это будет выглядеть на практике?

Мы можем рассмотреть классический случай цикла for, я сделал один и тот же цикл for дважды в typescript, чтобы показать более современную версию старого и, на мой взгляд, даже более красивую


// for loop classico
for (let i = 0; i < 30; i++) {
  console.log(i * i);
}
// for loop moderno  -> seu array = Array.from(Array(30).keys())
for (const [index, value] of Array.from(Array(30).keys()).entries()) {
  console.log(value * value);
}
// exemplo prático do array moderno
const array = [1, 2, 3, 4, 5, 6, 7, 8];
for (const [index, value] of array.entries()) {
  console.log(value);
}

Войдите в полноэкранный режим Выход из полноэкранного режима

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

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


;; resolver função que faz o quadrado dos primeiros 30 numeros inteiros
(defn create-n-list
  "creates a n list"
  [numbers]
  (take (+ numbers 1) (range)))

(defn first-n-multiples "recebe n numeros e retorna uma lista com seus multiplos"
  [numbers]
  (map * (create-n-list numbers) (create-n-list numbers)))

;; (first-n-multiples 30)
;;=> (0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441 484 529 576 625 676 729 784 841 900)

Войдите в полноэкранный режим Выход из полноэкранного режима

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

Если у вас возникнут трудности с чтением этого кода на Clojure, напишите мне DM в twitter, я буду очень рад помочь вам! Наибольшую трудность для многих людей при работе с clojure представляет его инфиксная нотация, как в примере с умножением (* n n).

Заключение

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

Оцените статью
devanswers.ru
Добавить комментарий