Comments 46

Я разуму уму заря,
Я иду с мечем судия;
С начала та ж я и с конца
И всеми чтуся за Отца.


Державин, 1805г


Что вас смущает?

Только то, что мы сейчас в 2019 году. И сейчас либо «мечём», либо «мечом». И в обоих случаях фраза уже не палиндром. Просто для примера можно было бы выбрать однозначно читающийся палиндром, а таких полно, хоть про Азора и его лапу ))
оффтопик
Любопытно, что у Державина была-то в оригинале «ё» наверняка, он был среди тех, кто её продвигал. А потерялась она при перепечатке, уже в наше время.

Какая разница, какой сейчас год если фраза написана 200 лет назад? А данная фраза широко известна и знаменита тем, что это первый достоверно известный авторский палиндром на русском. Потому и является отличным примером.


Любопытно, что у Державина была-то в оригинале «ё»

Вы в курсе, что в русском допустимо вместо "ё" писать "е"? У Державина там еще и "ъ" были.

Вы в курсе, что в русском допустимо вместо «ё» писать «е»?

Вы про современный русский? А «мечем» вместо «мечом» тоже допустимо? Закругляюсь, полнейший оффтопик, свою т.з. я высказал, к теме публикации всё это отношения не имеет.
Я извиняюсь! Как бы это иллюстративный пример, речь шла о программировании, а не о русском языке. Фразу я взял с тестов к задаче, которую мы даём на собеседовании. Как она туда попала я не знаю, уж извините.
Это все забавно, но изначальный вариант выглядит и читается намного проще ОО.
Особенно если чуть порефакторить, чтобы избежать дублирования с регуляркой
Прочитайте ТЗ пожалуйста. Задача создать набор инструментов, а не только две функции, т.е. мы не знаем точно как мы будем использовать элементы набора в коде, поэтому в итоге я приводил весь код с условным разделением на бандлы. На мой взгляд, в реалиях тех задач, которые были поставлены набор абстрактных функций выглядит лучше, чем 2 конкретные функции.

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

const allNotWordSymbolsRegexpGlobal = () => /[\.,\/#!$%\^&\*;:{}=\-_~()?\s]/g;
const replace = (regexp, replacement, str) => str.replace(regexp, replacement);
const toLowerCase = str => str.toLowerCase();
const stringReverse = str => str.split('').reverse().join('');
const isStringsEqual = (strA, strB) => strA === strB;


Это пример хелпера для каких либо целей.

Далее мы используя ФП инкапсулируем определённую бизнес логику в продуктовые функции.

Опять же, данный код не более чем обучающий пример на темы композиция, каррирование, частичное применение. В качестве допущения для данной задачи было выбрано: необходимость достижения функциональной чистоты. Кстати, вполне вероятная задача на собеседованиях.
Нет-нет. Вы усложнили функции, чтобы они подошли под инструмент.
Вот к примеру:
const toLowerCase = str => str.toLowerCase();
В чем смысл? Никакой дополнительной ценности, кроме того, что можно юзать с вашим новым инструментом. Но код вы пишите для инструмента или для решения проблем?
Это переход к функциональному стилю, т.е. к бесточечному коду на верхнем уровне. Довольно распространённая практика.

Вот почитайте.

Такой подход очень часто применяется в совокупности с теми концепциями, которые я описал в статье.
IMHO, гораздо интересней и проще использовать пайп + object in — object out
Вы опять же не поняли. Разумеется, на проекте вы не будете реализовать сами curry, partial и прочее, а просто подключите библиотеку.

В статье я реализовал всё нативно для того чтобы показать как эти вещи устроены под капотом.

Цитата из статьи:
Заключение

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

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

К слову, вы можете продолжить развитие этого модуля работы с палиндромами и развить его идеи, например загружать строки по апи, преобразовывать в наборы букв и отправлять на сервер, где будет генерироваться строка палиндром и многое другое… На ваше усмотрение.

Также неплохо было бы избавится от дублирования в процессах этих строк:

replaceAllNotWordSymbolsToEmpltyGlobal,
toLowerCase,

В общем, совершенствовать код можно и нужно постоянно!
т.е. к бесточечному коду на верхнем уровне

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

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

Можно побыть занудой?
В регекспах в квадратных скобках совсем другой синтаксис.
* экранировать не нужно, . — тоже.
^ если стоит не первым символом — тоже.
- не нужно экранировать, если поставить сразу после открывающей скобки или перед закрывающей.


Регекспы и так тяжело читать. А когда в них экранируется что нужно и не нужно — тем более.


P.S. И небольшое grammar-занудство: правильно areStringsEqual потому что to be во множественном числе становится are.

Во-вторых, функция getPalindrom обращается к базе.

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


К слову, аналог getPalindrom в функциональном стиле вы так и не написали.


В-третьих, функции модифицируют свои аргументы. Итого, наши функции не чисты.

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

В реальных проектах обращения к баз выносят отдельно, а логику отдельно. ФП это лишь инструмент. Применять его можно по разному и не всегда оно нужно. Например, молотком и гвоздями можно как человека к забору прибить, так и картину на стену повесить.

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

Вот именно. А вы почему-то пишите, что текущая реализация плоха с точки зрения ФП, потому что там идёт обращение к базе. Но если логика обращения к базе в этой самой функции, то она плоха в принципе, не взирая ни на какую парадигму.


И вообще, о чём эта статья? Показать плюсы ФП? Ну что ж, вместо функции на 3-5 строк у вас вышло кода в 3-4 раза больше, он читается труднее. Не понятно, зачем всё переписывать в функциональном стиле.


Вот тут, например, очень хорошо показаны минусы императивного подхода и плюсы функциональщины на примере. А у вас какая-то антиреклама ФП.

Статья о трёх концепциях ФП. Как видите, я реализовал композицию, каррирование и частичное применение нативными средствами. Остальное лишь иллюстративный пример. Данный код нужно воспринимать как код задачи.

На проекте всё слишком специфично и зависит от архитектуры. Для каких — то проектов организация кода в статье — антиреклами ФП, а для каких-то очень даже уместно.

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

Как мне кажется, преимущества ФП надо показывать на тех примерах, где ФП действительно помогает. А сейчас, получается, натянули сову на глобус.
Видел я один проект, который полностью написанн в функциональном стиле функциональной версии lodash. Прямо Lodash Головного Мозга. И, честно говоря, от проекта остались ровно такие же ощущения, как от этой статьи: вместо простых и понятных функций получаем ворох функций, функций-аргументов, функций-связок, функций-функций. Да, все чисто, по канону, но не удобно для чтения, а порой и поддержки.

Вы очень упорный человек — упорно пишите «palindrom» вместо «palindrome», упорно набиваете десятки чистых функций, где можно обойтись одним циклом без доп. памяти! ~~Респект таким парням~~ Честь и хвала таким людям!
Понимаете, ничего не существует в вакууме. Применительно к программированию любой код служит определённым целям. Данный код и данный пример преследует учебные цели: проиллюстрировать определённые техники и практики.

На реальных проектах другие законы. Люди опытные в функциональном программировании не будут читать такие простые статьи, они им не нужны, а для постигающих эти концепции будет простой и понятный пример, который, кстати, может встретится на собеседовании.
Для меня лично функциональное программирование в JS заканчивается на применении его огрызков в виде forEach/map/reduce/filter/every/some. Сильно не люблю код с циклами и с переменными-счётчиками, которые можно заменить использованием этих функций.

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

Мысль писать весь код в функциональном стиле вызывает… сильное отторжение. Подозреваю, что это результат воспитания и образования, когда серьёзно погружаться в программирование начинаешь с pascal/c++. Возможно, поколение, которое только-только начинает изучать программирование, при наличии всех современных языков, будет иначе воспринимать ФП.

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

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

P.S. Совсем иначе эту статью можно воспринимать, если в примерах кода начать применять pipeline оператор из ES2019 proposal github.com/tc39/proposal-pipeline-operator
С ним чистое ФП или его отдельные элементы выглядят совсем иначе, гораздо понятнее и лаконичнее.

P.P.S В подобных статьях имхо стоит через строчку писать, что это упрощенный учебный пример.
ФП очень хорошо подходит для организации утилей и хелперов на реальных проектах. Разумеется бизнес логику на ФП не нужно организовывать, а вот «набор инструментов для той или иной задачи» в хелперах или утилях будет правильным реализовать на ФП.
Разумеется бизнес логику на ФП не нужно организовывать

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

Ну в нашем случае это backend для соц. сети. А вообще не принципиально.

А в чем преимущество ФЯП перед «обычными» ЯП (в тех предметных областях, про которые вы знаете)? Надежней, легче масштабировать, или что-то другое? Именно плюсы, о которых говорят сами разработчики…

Ну это не меня надо спрашивать, на самом деле. Я ФП использую лишь отчасти. Я полагаю что киллер фича — декларативность. Есть строгая чёткая схема того как и почему что происходит. А те места где творится магия вынесены в свои собственные загончики, которые явным образом помечены. Это как unsafe в Rust. Проще дебажить, больше гарантий уже на уровне компиляции, понимание что сюрпризов будет куда меньше, и, как часто говоря ФП разработчики, больше дисциплины, т.к. эти искусственные ограничения принуждают разделять мух от котлет.


На на самом деле вам лучше спросить тех кто на ФП языках пишет. Я "не настоящий сварщик" :)

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


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


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


Потому писать в функциональном стиле на императивных языках можно, но каких-либо особенных преимуществ это не дает. Иногда код получается проще, но не всегда (см. статью про собеседование в Яндекс, где превозносится нечитаемая лапша с map/filter).

Так а разве код, написанный в функциональном стиле в императивном языке, трудно распараллелить? Если только его параллелить… Хотя, это уже может зависеть от конкретного ЯП (интерпретируемый-компилируемый), его компилятора и т.д.

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

Статью пропустил, надо почитать…
Свойства PF:… Не используют глобальные значения

Pure function может использовать глобалки. Никаких проблем с этим пока они иммутабельны. Откуда вы это взяли?


В-третьих, функции модифицируют свои аргументы

function isPalindrom (str) {
  const regexp = /[\.,\/#!$%\^&\*;:{}=\-_`~()?\s]/g;
  str = str.replace(regexp, '').toLowerCase();
  return str === str.split('').reverse().join('');
}

Где вы тут модифицируете аргументы? str = str.replace не модифицирует аргумент. Ваш аргумент остался нетронутым, вы просто потеряли с ним связь. Некрасиво с точки зрения ФП и чистоты кода, но не более того. И .reverse() тоже ничего не нарушает, ибо мутирует созданную в пределах вашей функции сущность (.split()). Эта функция не нарушает правил pure functions. Хотя конечно же лучше было присвоить это значение другому наименованию.

const allNotWordSymbolsRegexpGlobal = () => /[\.,\/#!$%\^&\*;:{}=\-_~()?\s]/g;//(1)

Что это? Зачем вы статику засунули в метод? Что мешало сделать стандартно: const BLABLA = /blabla/?


const replace = (regexp, replacement, str) => str.replace(regexp, replacement);//(2)
const toLowerCase = str => str.toLowerCase();//(3)

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


const isStringsEqual = (strA, strB) => strA === strB;//(5)

Синтаксический сахар в виде операндов придуман как раз для того, чтобы не писать такой код. Тем более когда у нас есть |> оператор.


ФП оно не про то как создать 100500 однострочных методов делающих очевидные вещи. ФП призван упростить сложное за счёт более понятной схемы взаимодействия и трансформации данных. В вашем примере вы взяли простое решение (причём isPalindrom уже был PF) и раздули его до груды кода, которая не должна пройти code review.


Поэтому в вас столько негативных комментариев.

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

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

На реальных проектах другие законы. Люди опытные в функциональном программировании не будут читать такие простые статьи, они им не нужны, а для постигающих эти концепции будет простой и понятный пример, который, кстати, может встретится на собеседовании.
Only those users with full accounts are able to leave comments. Log in, please.