Comments 15
.filter(x => x > 3), все, конец. Просто и понятно.
Я это к тому, что может быть будет лучше в конце статьи указывать более реальное применение всему сказанному? Найдутся ведь те, кто будет весь свой код переписывать на isBiggerThan(3)
Как раз недавно писал перевод статьи о каррировании в рамде. С нею всё становится ещё удобнее.
А потом проходит время, и вот ты уже недоуменно читаешь чьи-то дифирамбы про то, как же просто и быстро проходит обновление данных модели в ангуляре (просто — да, для пользователя фреймворка, но вообще-то нет; быстро — гкхммммм).
Вообще автор транслирует разумную мысль (если можно улучшить читаемость нетривиального кода, не теряя других важных качеств — то её стоит улучшить), только конечно заданная тема статьи этому мешает. Во-первых, не всегда читаемость повышается именно через каррирование. Во-вторых, демонстрировать это стоит все же не на функциях а-ля plusOne(), ну серьезно, тут все взрослые люди.
Сеньоры, по вашему опыту ведения крупных проектов, вы действительно поощряете переходы подобные этому: .filter(int => isEven(int))
-> .filter(isEven)
? Разве это не усложняет разработку? Да, выглядит более емко, но как быть с const csum
, вот это что вообще? Какие-то неоднозначные нейминги, опускание типов, где они, по сути, вообще не мешают, а лишь экономят драгоценные секунды чтения кода. Ради чего?
Но если есть подходящий контекст, где подобные цепочки преобразований ожидаются часто и обильно, то вполне можно заняться делом уменьшения повторов. То есть, у нас в проекте, например, объектная обёртка над canvas написана, естественно, с возвратом самой себя везде (это не каррирование как таковое, но тоже уменьшает повторы и увеличивает читаемость), чтоб можно было писать canvas.то().сё().иЕщеВотЭто().
Готов поспорить, что второй вариант отличается гораздо лучшей читабельностью, чем первый.
Нет, ни капли не лучшей. В первом сразу понятно, что происходит, а во втором приходится вникать в эту "магию".
Непонятно, зачем вообще тратить столько сил ради экономии считанных символов.
Правило №1
Две следующих конструкции эквивалентны:
Вообще-то нет, по крайней мере в общем случае. ['1','2','3'].map(parseInt)
отличный пример того, как можно напороться (и потратить долгие часы в отладке).
P.S.
Очень хочется верить, что однажды люди всех вероисповеданий языков узнают про The Zen of Python. В данном случае он весьма уместен, к примеру:
Явное лучше, чем неявное.
Простое лучше, чем сложное.
Если реализацию сложно объяснить — идея плоха.
Кстати о питоне.
Лямбды и генераторы связывают переменные по имени, что в сочетании с изменяемостью данных означает "штирлиц знал, что запоминается последнее".
На это можно очень больно наступить:
plusators = [(lambda x: x+d) for d in range(10)]
print(plusators[5](100)) # 109, wtf?!
# сразу выполнил тета-подстановку, чтобы не путаться между одноимёнными переменными
plusators = [(lambda c: (lambda x: x+c))(d) for d in range(10)]
print(plusators[5](100)) # 105
# вышло страшненько, не правда ли?
# вот так выглядит чуть менее страшно
plusator = lambda c: lambda x: x+c
plusators = [plusator(d) for d in range(10)]
# а вот так выглядит в соответствие с PEP8
# где сказано "даёшь функции вместо переменных с присвоенными лямбдами"
def plusator(c):
return lambda x: x+c
plusators = [plusator(d) for d in range(10)]
# четыре строчки вместо одной, больше читаемости богу читаемости
Готов поспорить, что второй вариант отличается гораздо лучшей читабельностью, чем первый», — говорит автор материала, перевод которого мы сегодня публикуем. По его словам — всё дело в аргументах методов filter() и map().
В каком это мире данный код выглядит более читабельнее? Сейчас бы элементарные (даже элементарными назвать с натяжкой можно) арифметические выражения запихивать в отдельные функции. С какой целью это делается? Читабельнее код становится? Т.е. если человек посмотрит и увидит + 1 он не поймёт, а если прочитает plusOne ему всё сразу станет ясно? Где то логика умерла.
Точно такой же подход я использовал в своей библиотечке для ActionScript, но причина была только в том, что там не было arrow-functions:
// 1. using custom callback
var result:Stream = s.filter(function(item:Item):Boolean{
return item.price > 1;
});
// 2. using provided global functions(can compare Boolean, Number, Date, XML, String types)
// supports nested properties i.e. member("prop.prop.prop")
var result:Stream = s.filter(gt(member("price"), 1));
// 3. using Proxy magick and global functions
var result:Stream = s.price(gt(_, 1));
Имеет смысл так делать только для каких-нибудь сложных агрегатов или типовых операций над строками.
Скажем .filter(not(isVowel))
лучше все же переписать как .filter(x => !isVowel(x))
.
Я приверженец первого варианта в плане "явности". Но есть два случая, когда второй вариант более уместен:
- в каждой функции-колбеке замешано большое количество логики
- вся вот эта цепочка вызывается тысячи раз — в таком случае банально экономится память, выделяемая под анонимные функции (как получается в первом варианте)
Как можно понять, не существует серебряной пули — каждый вариант имеет право на существование; все дело в контексте.
У меня в одном проекте подрядчики напилили код как в правом столбике. Только для методов массива они также юзали свои функции.
Я долго привыкал и до сих пор не уверен что это полезный подход по части поддерживаемости и читабельности.
Секреты JavaScript-кухни: специи