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

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

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

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

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

Когда мы вводим подобные повседневные модели в наше сознание, мы можем исследовать их с помощью строгих методов, которые мы разработали в других дисциплинах, где мы формально моделируем. Функциональное программирование - одна из таких дисциплин.

Функциональное программирование помогло мне задуматься о том, почему я счастлив или несчастен в данный момент. Используя простое программирование и математические примеры (например, 1 + 2 = 3), мы исследуем, как практики функционального программирования могут быть применены к нашему образу мышления.

Мы окружены функциями

Функция - это процесс, который принимает входные данные и возвращает выходные данные. Например, функция addTwo выдала бы нам выход 4, если бы задан вход 2.

В современном JavaScript мы могли бы написать эту функцию как:

const addTwo = (input) => input + 2

В математике эту функцию можно описать так:

f(x) = x + 2

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

Мы взаимодействуем с функциями в повседневной жизни, но формулы скрыты от нас. Например, вы можете постоянно проверять свой телефон в поисках ответа на отправленное вами текстовое сообщение, которое, по вашему мнению, было довольно умным. Или это было глупо? В этот момент разговор - самая важная функция перед вами. Вы отправляете сообщения в качестве входов и получаете (с задержкой 😒) сообщения в качестве выходов. Если бы вы только могли расшифровать формулу разговора, возможно, ваши отношения с другим человеком могли бы быть более… функциональными.

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

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

Делаем функции чистыми

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

let a = 2
const addTwo = (input) => input + a

addTwo является «недетерминированным», что означает, что мы не можем гарантировать, что каждый раз будем получать один и тот же результат при одном и том же вводе. Например, если мы дадим 3 в качестве входных данных для addTwo, он выдаст 5. Однако, если кто-то установит значение a на 100, то внезапно вход 3 вернет 103. Это может нас удивить, потому что мы использовали ту же функцию с тот же ввод, но что-то другое.

Точно так же в жизни мы часто удивляемся, когда наше настроение внезапно меняется, даже если исходные данные не изменились. Когда это происходит, возможно, что функция, которую мы разработали для моделирования нашего счастья (назовем ее determineHappiness), явно не учитывает входные данные.

Например, когда я в детстве чем-то расстраивался, мама часто говорила, что я просто голоден. Другими словами, она утверждала, что моя determineHappiness функция была нечистой.

const hungry = true
const determineHappiness = (input) => ...complex logic that returns an unhappier state when hungry is true

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

const sunPosition = 'right above me'
determineHappiness(sunPosition)

sunPosition is ‘right above me’ подразумевает, что сейчас полдень, что, в свою очередь, означает, что у меня много времени, чтобы поиграть, а это значит, что я должен быть счастлив. Если я даже тогда несчастен, возможно, есть еще что-то, что я не учел, что также влияет на мое счастье. Если это правда, что я давно не ел, возможно, моя функция также должна принимать hungry в качестве входных данных.

const hungry = true
const sunPosition = 'right above me'
determineHappiness(sunPosition, hungry)

Но можем ли мы с уверенностью сказать, что мое счастье зависит только от того, голоден ли я и от положения солнца? Очевидно нет. Если мы хотим, чтобы determineHappiness был действительно чистым, мы должны учитывать положение солнца, голод и миллион других вещей, которые составляют мой контекст, как входные данные для determineHappiness.

determineHappiness(context)

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

determineHappiness(sunPosition, hungry) достаточно просто, чтобы я знал две части моего контекста, которые оказывают заметное влияние на мое счастье.

Когда мы делаем вклады нашего счастья явными, мы даем себе возможность спросить: «Что я могу изменить в своем окружении, чтобы стать более счастливым человеком?» Это кажется простым, но я считаю это упражнение заземляющим, когда я одновременно сталкиваюсь с множеством сложных задач.

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

Помните, что вклады в наше счастье постоянно меняются, поэтому важно, чтобы мы постоянно переоценивали вклады в нашу модель счастья.

В поисках абстракций

Как правило, всякий раз, когда программисты видят дублирование, они пытаются его удалить, чтобы код было легче изменять и поддерживать. Представьте, что у вас есть три функции: add2, add3 и add4.

const add2 = (input) => input + 2
const add3 = (input) => input + 3
const add4 = (input) => input + 4

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

const add = (input, other) => input + other

add - это абстракция. Мы обнаружили общий принцип, который управляет add2, add3 и add4, обратив внимание на шаблон, который гласит, что все эти функции могут быть добавлены. Умение абстрагироваться позволяет нам одинаково относиться к самым разным вещам. Веревка и манго очень разные, но их можно разрезать. Создание этой абстракции позволяет нам использовать один и тот же инструмент, нож, для работы с очень разными вещами. Но при работе с эмоциями бывает трудно переварить абстракции.

Однажды, когда мне было очень грустно из-за разрыва, моя подруга попыталась утешить меня, сравнив это с тем, что она чувствовала, когда потеряла свою любимую толстовку с капюшоном. Я чувствовал себя оскорбленным, потому что она сравнивала мою потерю с такой обыденной вещью, как потерянная одежда. Но в некотором смысле сравнение было справедливым. Оба опыта разделяют абстракцию потери чего-то, к чему человек привязан.

Хотя разрыв мог быть для меня новым опытом, потеря - нет. Как и нож, я мог повторно использовать инструменты, чтобы справиться с незнакомой ситуацией, распознавая знакомую абстракцию.

Сочувствие - это признание общей абстракции между вашим опытом и опытом другого человека. Когда кто-то говорит вам: «Я понимаю, через что вы проходите», даже если у него не было такого опыта, он делает именно это.

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

Полиморфная функция

Теперь давайте посмотрим, сможем ли мы найти общую абстракцию между текстовым сообщением и положением солнца.

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

Давайте выделим общую interpret функцию и будем рассматривать ее как еще один фактор, определяющий наше счастье.

determineHappiness(context, interpret)

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

Оптимист или пессимист сделает еще один шаг вперед и сопоставит все с одним состоянием счастья, счастливым или несчастным, соответственно.

const optimisticInterpret = (anything) => "happy"

Рассматривая interpret как фактор, определяющий наше счастье, мы предполагаем, что даже в одном и том же контексте разные взгляды приведут к разным состояниям счастья.

В течение долгого времени эта точка зрения приводила меня к мысли, что я должен сосредоточить свои усилия на изменении interpret, потому что interpret находится в моей голове, тогда как context находится во внешнем мире. Теоретически, interpret больше под моим контролем, чем положение солнца или даже голод.

Но грань между context и interpret размыта. Наши прошлые контексты живут так, как мы интерпретируем наш текущий контекст. Если вчера я сильно обжег руку, контекст пожара может исчезнуть, но он все равно повлияет на то, как я буду реагировать на вещи, к которым я прикасаюсь сегодня.

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

Ум - невероятное место. Это вселенная, пропущенная через наши чувства. Там есть вулканы и научные факты для шестиклассников. В нем есть сожаления, тексты песен и тон голоса вашего друга, когда вы последний раз звонили ему. И у него есть наблюдатель, который за всем этим следит. Этот бедный дурак несет печальную ответственность за считанные секунды набросать все, что находится перед ним. Можем ли мы винить их в том, что они пропустили запятую или комету?

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

Ум - это наш инструмент для понимания, но, пожалуй, это самая трудная вещь для понимания. Но, в отличие от большинства вещей, понимания ума достаточно, чтобы изменить то, как он работает.