Как стать автором
Обновить

Комментарии 35

Интересно терминологию перекосило. Было "functions are first-class citizens", а теперь "functions are first-class functions" (проверил, в англоязычных источниках та же фигня).

Да, поэтому надо держать руку на пульсе, сейчас уже в разговоре с англоязычными разработчиками некоторые термины не употребить.
Почему в JS сейчас так форсят так тему ФП?
потому что ее везде форсят. А форсят ее потому, что она более понятна чем ООП, менее багованый код на ней и легче подается многопоточному программированию.
НЛО прилетело и опубликовало эту надпись здесь
Извиняюсь, можно вопросы «для чайников в ФП»?

1) Почему решили писать так:
const add = (x,y) => x+y;
const add = x => y => x + y;
вместо:
const add(x,y) => x+y;
const add(x,y) => y => x + y;
Как по мне, второй вариант как-то единообразнее и привычнее. В этом есть какой-то смысл или просто решили так?
2) Может я не понимаю, что такое замыкание, но откуда берется Y во втором выражении? Визуально не очевидно, что оно передается в функцию вместе с X.

3) В примере
const impureAddProp = (key, value, object) => {
  object[key] = value;//Добавляем свойство объекту
};
const User= {
  name: 'Alex'
};
impureAddProp ('isAdmin', true, User);
компилятор будет ругаться?

4) В примере
const pureAddProp = (key, value, object) => ({
  ...object,
  [key]: value
});
const User= {
  name: 'Alex'
};
const Admin= pureAddProp ('isAdmin', true, User);
разве объект Admin не делит свое состояние с объектом User (имея в виду, что оба они могут быть в реальной жизни намного сложнее)?
Или «состояние» — это не значение в рантайме, а код в рантайме? Но тогда не менять код объекта в рантайме это разве фича только ФП?
>второй вариант как-то единообразнее и привычнее

const add(x,y) => x+y;

const two= 2;

Во втором случае вы тоже предлагаете выбросить =?
Так я и спрашиваю — для константы, которая число, мы используем const имя = значение, а в этом случае вы предлагаете знак равенства выкинуть. Почему вам это кажется более единообразным, в то время как на самом деле ровно наоборот?
А если константу (которая число) потом надо будет изменить (по смыслу задачи) на функцию или переменную? Я то вот и думал, что фича ФП как раз в том и состоит, что там все равно: число, переменная или функция — механизм работы с ними один (функция).
Хм. Вообще-то смысл константы в том, что она больше не изменяется. То есть, применяя const вы определяете неизменную константу. И эта константа может быть в том числе и функцией.

Кстати насчет привычности — в скале, к примеру, все вообще ровно так же, кроме замены const на val. Так что вопрос привычек — он зависит от предыдущего опыта.
В ФП ничего не изменяется. Концепция имутабельности справедлива для всех сущностей ФП. Об этом и статья.
Вы ошибаетесь. ФП не ограничивается иммутабельностью, и вполне бывает без нее. Ну или если угодно, во вполне ФП языках вполне бывают mutable переменные.
Здравствуйте!

Код:
const add(x,y) => y => x + y;

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

Во втором пример Админ не делит состояние с Юзером т.к. свойства Юзера копируются в Админа по значению. В этом и заключается концепция Имутабельности в ФП.

Если вы в серьёз решили изучить ФП в разрезе JS, то мои статьи вам помогут. Пока что достаточно запомнить три описанные концепции. В дальнейшем будут более практические примеры и станет понятнее для чего эти основы(PF, Имутабельность) нужны.

1) Почему решили писать так:

Старая добрая запись никуда не делась


function add(x, y) {
  return x + y;
}

ее по-прежнему можно использовать, если штука с const неудобно читается.


2) Может я не понимаю, что такое замыкание, но откуда берется Y во втором выражении? Визуально не очевидно, что оно передается в функцию вместе с X.

То, что здесь происходит, называется каррированием. Вот тут есть отдельная статья с объяснением, что это такое


3) В примере компилятор будет ругаться?

Какой компилятор? В Javascript ругаться точно не будет, но в typescript можно объявить объект неизменяемым и получить ошибку.


const User = { name: 'Alex' } as const; // неизменяемый объект

User.name = 'Boris'; // Error: Cannot assign to 'name' because it is a read-only property.(2540)

// ошибок компиляции нет 
const NewUser = { ...User, name: 'Boris' }; 

Работающий пример.


Конструкция as const – это специальный синтаксис Typescript, которого в обычном Javascript нет. Документация вот тут.


4) разве объект Admin не делит свое состояние с объектом User (имея в виду, что оба они могут быть в реальной жизни намного сложнее)?

Все верно, если у вас внутри есть вложенные объекты или массивы, их тоже нужно не забыть склонировать.


const user = {
   name: 'Alex',
   location: {
      country: 'RU'
   }
}

// не забываем склонировать location
const newUser = { ...user, location: {...user.location}, name: 'Boris' };

Каждый раз писать такой код может оказаться утомительно, поэтому можно взять утилиту, вроде lodash.cloneDeep.

Здравствуйте! В рамках статей я сознательно использую нативный js, чтобы показать как та или иная концепция работает. Разумеется, на проекте мы все используем готовые инструменты. Авторы этого коммента я не ответил т.к. очевидно, что он не понял тему.
Спасибо за развернутый ответ.

Старая добрая запись никуда не делась… ее по-прежнему можно использовать, если штука с const неудобно читается.
Читается почти удобно, просто синтаксис кажется несколько избыточным…

То, что здесь происходит, называется каррированием. Вот тут есть отдельная статья с объяснением, что это такое
Спасибо, хорошая статья.

Какой компилятор?
Извиняюсь, это я из контекста выпал…

Конструкция as const – это специальный синтаксис Typescript, которого в обычном Javascript нет.
Приятная конструкция.

Все верно, если у вас внутри есть вложенные объекты или массивы, их тоже нужно не забыть склонировать.
Просто в статье было написано «Мы изменяем копию данных, а это всегда безопасно». Но это уже придирки, главное понять что к чему.

Почему решили писать так

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


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

Так оно же передаётся не "вместе", а очень даже раздельно. Функция add принимает x и возвращает анонимную функцию, которая уже принимает y.


Возможно, с лишней парой скобок будет понятнее?


const add = x => (y => x + y);

const add = x => {
    const addx = y => x + y;
    return addx;
}

1) Если написать код так, как вам хотелось бы — будет просто SyntaxError. Похожим на желаемый синтаксис будет определение именованной функции с помощью function name(arg) {}.


Это работает, т.к. вы дали функции имя, чтобы ее можно было использовать в дальнейшем. В случае с лямбда-функциями (которые в JS описываются "стрелочным" синтаксисом вроде a => a + 1) — их не просто так называют анонимными.


При объявлении они не получают имени и поэтому должны быть где-то сохранены, чтобы их можно было использовать в дальнейшем. В данном случае их сохраняют в константу. Поэтому нам нужно объявить константу так же, как обычно. Если в примере const obj = {a: 1} мы сохранили в константу obj ссылку на объект { a: 1 }, то в const func = (x, y) => x + y мы сохраняем в константу func ссылку на объект функции.


Это единый и привычный синтаксис для данного использования лямбда-функций. Мы также можем объявить лямбда-функцию прямо в аргументах некой функции. Тогда она будет сохранена в замыкании. Пример:


function onClick(elId, callback) {
  if (elId == 1) {
    callback()
  }
}

onClick(1, () => console.log('Вызван коллбек'))

Мы объявляем анонимную функцию, не сохраняя ее в константу. Она будет доступна внутри функции onClick под именем callback.


3) У обычного js в целом нет компилятора — это интерпретируемый язык. Данный пример не несет в себе ошибке и НЕ является плохим по определению. Это плохой код только в парадигме функционального программирования, при условии, что есть возможность написать "чистую" функцию.


4) Ответ автора не совсем верен. Как вы правильно упомянули, структура данных User может быть достаточно сложна. В этом случае ...object скопирует в новый объект только поля и значения первого уровня. Если в нем будут вложенные объекты — они НЕ будут скопированы, и могут изменяться, если какой-нибудь код будет иметь к нему доступ. Пример:


let a = { c: 1 }
let b = { m: 1, a }
let d = { ...b, k: 1 }
a.c = 2
b.m = 2
console.log(d) // { m: 1, a: { c: 2 }, k: 1 }

Как видно, объект a, вложенный в b, а потом в d при изменении повлиял на d, тогда как при изменении поля m, которое находится на верхнем уровне объекта b, объект d не изменился.


Таким образом, для иммутабельности стоит использовать специальные либы, которые делают "глубокие" копии для объектов, массивов и т.д. В простом варианте это lodash с его cloneDeep или более специализированная Immutable.js

const add = x => y => x + y;

Нет, это не коротко и лаконично, за такое надо руки выравнивать. Особенно когда IDE предлагает add(x) с одним аргументом, и в ответку тебе функция летит. Это очень странно так писать add(1)(2).
const add = (x,y) => x+y
А то что приведено выше, обычно используется для инкапсуляции, например так:


const throttle = action => {
    let isRunning = false;
    return () => {
        if (isRunning) return;
        isRunning = true;
        setTimeout(() => {
            action();
            isRunning = false;
        }, 10000);
    }
};
let throttled = throttle(() => console.log(4));
throttled();throttled();throttled() // callback сработает только один раз

В примере выше инкапсулируется isRunning. В примере со сложением явный оверинжиниринг

const add = x => y => x + y;


Это не более чем пример лямбда функции. Иллюстрация концепции.
Двух лямбда функций, я бы сказал…
То что вам это странно — не значит, что это бессмысленно.
const add = x => y => x + y


Такой прием делается не просто для краткости кода, а как минимум для создания:

— композиции функций
— передачи переменных в скоуп функции

к примеру, есть колбэк функция onClick:

<button onClick={}>Delete</button>


и нам необходимо создать универсальную функцию удаления по id, однако функция onClick принимает только один параметр event:

const onClick = event => {
  event.preventDefault()
  remove(id)
}


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

Вот для таких случаях подходит этот прием «функция которая возращает другую функцию»


const deleteOnClick = ({id, remove}) => event => {
  event.preventDefault()
  remove(id)
}
 
const DeleteUser = props => (
  <button onClick={deleteOnClick(props)}>
    Delete
  </button>
)

const DeleteProduct = props => (
  <button onClick={deleteOnClick(props)}>
    Delete
  </button>
)


и второй случай:


const add = x => y => x + y
const compose = (...fns) => fns.reduce((a, b) => (...args) => a(b(...args)))
const update = (prop, updater) => obj => ({...obj, [prop]: updater(obj[prop])})
const now = () => new Date

const likePost = compose(
  update(`date`, now),
  update(`likes`, add(1)),
)

const post = {id: 1, title: 'Learn FP & love it', date: new Date('2019-01-01'), likes: 130}

likePost(post)


В этом примере мы создаем новую функцию likePost на основе уже двух существующих функции.
Например, следующая статья будет о ключевых понятиях ООП: инкапсуляции, абстракции, примесях(и штрихах), интерфейсах и т.д
И будет очередная статья о том, что уже написано тысячи раз.
Лучше бы расширили свой кругозор в ООП и написали о том, с чем большинство веб-разработчиков не знакомы.
Знаете, я начал писать статьи потому что мне в обязанности на работе вменили подтягивать наших джунов)

Я просто вижу в каких вещах у разработчиков определённого уровня пробелы и решил написать цикл статей, закрывающих эти пробелы. Убить двух зайцев так сказать.
НЛО прилетело и опубликовало эту надпись здесь
Можно сразу и учебников с десяток «понадовать». Парочку по алгоритмам, парочку по ООП, бессмертный труд Фленагана по яваскрипт, десяток книг по паттернам, матан и введение в ФП и т.д.

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

Например, лишних сущностей можно как в ФП, так и в ООП наплодить.
На такой общий вопрос может быть только очень общий ответ. Ответ этот — бывает по разному. Если надо менять много объектов, то иммутабельность может сильно ударить по производительности (я такое видел в реальном проекте). С другой стороны иммутабельность дает бонус для многопопточного кода. Но с третьей стороны, не всякий многопоточный код имеет общее состояние и этот бонус реально нужен. Тут уж все зависит от конкретной задачи.
Так и есть! я не пропагандирую применять ФП везде и всюду! Это лишь инструмент.

Молотком, гвоздями и лопатой можно как человека бить, так и сарай на даче построить. Это уже на совести человека, как распорядится инструментами.
Это все понятно про инструмент и все такое. Это банальность. ИНтересный вопрос в другом — для каких проектов ФП подходит, а для каких не очень. Хотя бы в теории, а лучше с примерами. Мечты, мечты…
На мой взгляд, вопрос стоит не так! Не «для каких проектов», а «для каких частей проекта».

Например, на текущем проекте у нас действует следующее соглашение:
— в утилях и хелперах только фп
— в колбеках только фп
— в вотчерах компонентов только фп
— в фабриках старые добрые декларации функций
— моделях ес6 классы

На мой взгляд, удобно.
На мой взгляд, вопрос стоит не так! Не «для каких проектов», а «для каких частей проекта».

Да, так будет точнее сформулировано.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации