-
Послушайте, что пишет психотерапевт
Наткнулся на интересную статью одного психотерапевта. Говорит грамотные вещи, хорошо причищает мозги. Позволю себе процитировать самое интересное с комментариями.
О детях:
Ребенка надо выращивать, а не воспитывать. Воспитание – это ограничение. И самое тяжкое ограничение – это запрет мыслить. Потому что если ребенок начнет делать что-то свое, непонятное учителям, они ему поставят двойку. Вот я помог младшему сыну решить задачу по математике. А сын потом говорит, неправильно, у нас учительница не так решала. И в следующий раз он будет уже не задачу решать, а угадывать, что нужно учителю.
Подпишусь кровью. Учителей, которые заворачивают домашнюю работу за то, что “решено не так”, надо вешать на столбах, чтоб неповадно было.
О праздниках:
Или когда у человека безрадостная жизнь, он начинает праздники праздновать. А это не по природе. По природе каждый день должен быть праздником.
Например, празднование Нового года. Оно обходится человеку в 100 тыс. долларов как минимум. Только я считаю не из того, сколько он сейчас зарабатывает, а сколько хотел бы и мог. Например, нормальный заработок психотерапевта в час – это 100 долларов. Но его можно достигнуть не сразу, в 18 лет, а в 25. Но если люди две недели до Нового года черте чем занимаются, две недели после, то и не дорастают до самих себя, конечно.
Праздников в Российском календаре слишком много. Это особенно заметно, когда начинаешь работать с Европой или США. Я не могу передать, как бесит перестановка рабочего времени из-за праздника. Например, работаем в субботу, чтобы отдыхать в понедельник. Очевидно же, что никто в субботу работать не будет. Биоритмы! Ну, а праздники с датами в названиях, вроде 8 Марта, 23 Февраля, 1, 9 Мая – это такой совок, что стыд. В этому году жена говорит – не поздравляй меня с 8 Марта, лучше в любой другой день, потому что это так уныло. А я и сам хотел об этом сказать. Так что теперь мы их не празднуем.
Про труд и любовь:
Элементарно просто. Интересный труд и любовь. Причем любовь на втором месте. У нас от любви ждут больше, чем она может дать. Вот я дом построил. Электричество есть, водопровод, отопление, канализация. Все, можно жить. Но нет обоев. А люди думают, что обои – это самое главное. Образно выражаясь, дерьма в голове много, и это дерьмо управляет поведением.
Интересный труд – это то, что реализует человека. Не случайно же Маслоу задвинул реализацию на самый верх. Человек, одержимый любимым делом, любит всех и любим всеми, даже если он по натуре засранец, как Стив Джобс.
О личностном и карьерном росте:
Нужно стать профессионалом экстра-класса. Тогда автоматически станешь полезным другим. Это еще Адам Смит говорил. Я вот книжки пишу. Рассказываю, как лучше жить, удобнее. Результаты есть – многие мои ученики круто вверх поднялись. Можно, оказывается, и в наших условиях успехов добиться. А когда перестал Дни рождения и новый год праздновать, хоть бы кто от общения со мной отказался.
Проще говоря, нужно не только накапливать знания, но и делиться ими.
О деньгах:
Когда человек приходит с любовной тягомотиной, я спрашиваю, сколько ты зарабатываешь. Научись зарабатывать пять тысяч долларов, а потом занимайся решением семейных проблем.
Тоже верно. Нам втирают, что деньги не главное, однако, деньги решают 90% всех проблем. Если взять типичную несчастную семью и начать копать, все упрется в стеснение в средствах.
О состоятельных женихах:
Идиотки. Вот пример приведу. У нас один ростовский олигарх женился на парикмахерше, снял ее с работы. А потом его застрелили. Ей 25 лет и пятилетний ребенок. Так я наблюдал, как она через несколько лет по миру пошла. Была бы обучена – удержала бы состоянием. А он говорил, зачем, я сам заработаю.
Таких печальных историй со знакомыми из детства я знаю не одну. Находят пузатых бандитов, купаются в шоколаде. Потом мужа сажают, убивают или тупо отжимают бизнес. Жене остается или выживать, или идти на сексуальное содержание к другому бизнесмену.
О проституции и морали:
Мужчина и женщина – это только когда речь идет о сексе и участии в деторождении. А когда речь идет о деле, тут кто лучше делает. У нас же женщин воспитывают в стиле латентной проституции. Она ищет мужчину, который бы ее защищал, кормил, жил для нее. А сама она может быть никем. Как это назвать? Мне обычная проституция кажется более честной, когда проститутка отрабатывает технику секса, получает деньги и не навязывается мужчине в постоянные спутницы жизни.
О сумашествии:
Это еще Ницше сказал, что сумасшествие единиц – исключение, сумасшествие масс – правило. Есть психотические расстройства – бред, галлюцинации, нелепое поведение. Таких сумасшедших немного, где-то шесть на тысячу.
Без комментариев, просто посмотрите выпуск новостей на ОРТ.
О тех, у кого все виноваты:
Этого я не знаю. Я не президент. Но к молодому и здоровому человеку, если у него что-то не получается, у меня никакого сострадания нет. Пойди на оптовый рынок, купи и потом продавай. Или на машине таксуй.
Занимательно, что столь интересный материал опубликован а бабском сайте, где все сплошь про макияж и фен-шуй. Бывает же.
-
Книга Atomic Scala и наивные впечатления от языка
Прочитал книжку о Скале, заодно немного поковырял язык. Это первая книжка, прочитанная мной на Киндле.
Понравилась структура. Примерно 30 глав – атомов – по разным аспектам языка. Автор – плюсовик со стажем, по ходу действия дает полезные советы, объясняет ООП-паттерны, затрагивает ФП.
Ближе к концу серия глав о монадах. Описаны
Either
,Maybe (Option)
,Try
. Понравилось, что автор ни разу не употребил термин “монада”. Это камень в огород Хаскела, где монады начинаются с аппликативных функторов.К книжке прилагается набор примеров с тестами, доками. Все добротно и на совесть, лежит на Гитхабе. Автор очень ответственно подошел к делу. По сути, у него не просто книга, а сообщество вокруг нее. Покупка книги – своего рода членский билет.
Я немного повозился с языком и могу сказать следующее. Прошу прощения за дилетантский взгляд, все пункты ниже могут оказаться ложью и провокацией.
-
Сперва язык кажется очень простым, вроде Гоу или Питона, только с типизацией. Примерно к середине книги понимаешь, что все не так просто.
-
Некоторые элементы дизайна очень странны. Например, составные объекты, когда объявлены класс и объект с одинаковыми именами. Я долго ломал голову, пока наконец понял, что таким образом формируется метакласс. Можно было сделать удобнее.
-
Избыточные модификаторы вроде
sealed
,implict
. Мне кажется, их впихнули задним числом, получилось не очень. Скала вроде стремится в минимализму, а получается опять Джава. -
Самая жесть с параметрическими трейтами. Напоминает Хаскел, но очень неуклюже. В книге есть пример как построить модель рецепта мороженного. 100 строк на декларацию трейтов, затем дикий микс из них. В конце автор пишет, что вот как хорошо получилось. Я бы убился поддерживать это мороженное.
-
Скала все-таки ООП-язык, хоть и с функциональным уклоном. Насколько я понял, в экосистеме Скалы не поощрается процедурно-модульное программирование. Получается
yet another oop language
.
Hieronymus Bosch “A visual guide to the Scala language” oil on oak panels, 1490-1510. The left panel shows the functional features, the main one describes the type system, and the right the object oriented parts.
Все же, интерес к языку у меня возник, и при случае был бы рад принять участие в проекте на нем.
Выборочные цитаты из книги с моим вольным переводом.
О ручной итерации:
Notice that
map
andreduce
take care of the iteration code that you normally write by hand. Although managing the iteration yourself might not seem like much effort, it’s one more error-prone detail, one more place to make a mistake (and since they’re so “obvious,” such mistakes are particularly hard to find).Обратите внимание,
map
иreduce
сами заботятся об итерации. В императивном подходе итерация ложится на ваши плечи. Это кажется пустяком, но увеличивает код и вероятность ошибки. Баг в ручной итерации замечаешь в последнюю очередь.О комментариях:
Comments should add new information that isn’t obvious from reading the code. If the comments just repeat what the code says, it becomes annoying (and people start ignoring your comments). When the code changes, programmers often forget to update comments, so it’s a good practice to use comments judiciously, mainly for highlighting tricky aspects of your code.
Комментарии должны дополнять код, объяснять неочевидные моменты. Раздражает, когда комментарий повторяет в точности то, что делает код. Люди проигнорируют их. Когда код меняется, программист часто забывает обновить комментарий. Комментируйте вдумчиво, в основном чтобы объяснить неочевидные приемы в коде.
О паттерн-матчинге:
Notice that pattern matching can overlap with the functionality of if statements. Because pattern matching is more flexible and powerful, we prefer it over if statements when there’s a choice.
Замечу, что паттер-матчинг похож на условное выражение. Первый более гибок, поэтому будем использовать его вместо условия при удобном случае.
В целом, книжка зачет, не жалею потраченного времени.
-
-
Картофельный мап
По следам прошлого поста о функции
Map
.Вам дали задачу – смоделировать процесс приготовлении картошки. Имеем набор картофелин. Каждая должна пройти стадию чистки, мойки, резки и варки.
Не вопрос, отвечет программист:
class Potato: def clean(self): pass def wash(self): pass def cut(self): pass def cook(self): pass potatos = [Potato(), Potato(), ...] for p in potatos: p.clean() p.wash() p.cut() p.cook()
Вроде ОК? Нет, все развалится.
Сперва попросят добавить временные задержки. Чистка – 10 секунд, мойка и резка – 5 секунд, варка – 15 минут (900 секунд). Впишем
time.sleep(N)
в нужные методы.... def clean(self): time.sleep(10) ...
Запустим код и убедимся, что 10 картошек варятся 150 минут! Конечно, ведь каждая картошка обрабатывается отдельно. Мы варим картошку поштучно. Так никто не делает.
Выходит, варить картошку в цикле нельзя. Должен быть особый код, который сварит картошки вне цикла. Класс картофелины не должен знать о технике варки. Значет, будет некий менеджер
CookManager.cook(potatos)
.Теперь скажут, что картошку готовы чистить два человека. В цикле выше это никак не предусмотрено. Придется заворачивать цикл в функцию и передавать часть списка:
def process(potatos): for p in potatos: ... potatos = [Potato(), Potato(), ...] process(potatos[:n]) process(potatos[n:]) CookManager.cook(potatos)
Поделили. Но код никак не учитывает, что в процессе чистки может подключиться еще один человек. Или наоборот, уйти. Перестроить списки в процессе невозможно.
Есть проблема серьезней: процесс мойки делить между людьми нельзя. Потому что мойка одна. Это значит, нужно выдергивать из цикла операцию
p.wash()
, и как-то все перетасовать. И не забыть про варку в конце.Короче, я к тому, что нужно сразу делать правильно:
class Potato: ... potatos = [...] map(Potato.clean, potatos) map(Potato.wash, potatos) map(Potato.cut, potatos) map(Potato.cook, potatos)
Правильно – значит, оставлять потенциал для абстракций. Пока что код выше технически не отличается от цикла. Картошка тоже вариться 150 минут. Но это легко исправить:
def map_threaded(func, seq): from multiprocessing.dummy import Pool pool = Pool(processes=len(seq)) return pool.map(func, *seq) ... map_threaded(Potato.cook, potatos)
Здесь мы определяем мап в тредах. Число тредов равно числу картошек. Это значит, все они будут готовиться параллельно, как в реальности в одной кастрюле. Питонячьи треды переключаются при
time.sleep()
. Засыпание равносильно IO-ожиданию. Поэтому все картошки приготовятся за 15 минут.Теперь чистка. Это будет мап на очередях. Воркеры – люди. Конфигурация очереди никак не влияет на код, поэтому можем подключить два, три и более воркеров со своими приоритетами и весами. Захотим, отключим воркер в процессе работы. Мап будет примерно такой:
def map_queued(func, seq): task = celery.task(func)(<task options...>) res_id_list = map(task.apply_async, seq) return map(task.get_result, res_id_list) map_queued(Potato.clean, potatos)
Мойку параллелить нельзя, оставляем обычный мап. Все вместе:
class Potato: ... potatos = [...] map_queued(Potato.clean, potatos) map(Potato.wash, potatos) map_queued(Potato.cut, potatos) map_threaded(Potato.cook, potatos)
Смысловая часть кода осталась неизменной. Мы просто поменяли один мап на другой. Всегда можем вернуть старый мап, если что-то пойдет не так. Функции map, filter, reduce, eval, apply – фундамент абстракций. Абстракции делают код открытым к изменеиям.
В идеале следует избавиться от классов, чтобы не хранить состояние картошки. Заменить методы на функции и пробрасывать результат предыдущего мапа в следующий.
-
Впечатления от электронной читалки Kindle
UPD продолжение: впечатления два года спустя.
Месяц-два назад заказал читалку Kindle Paperwhite. Очень доволен покупкой. Прочел пару книг, делюсь впечатлениями.
Читалка классная. Компактная, удобно читать, носить. От нее почти не устают глаза. Если читаешь вечером, утром не чуствуешь последствий. От айпада или монитора, наоборот, ощущения, что глаза засыпали песком. На солнце все видно без проблем.
Забавно, что дневной свет экономит питание электронной читалки, а на айпадах, наоборот, заставляет выкручивать яркость на максимум.
Заказывал в магазине www.elektrokniga.net. Контора напоминает шарашку. Например, вы не оплачиваете товар, а переводите деньги физлицу на карту. Но обошлось, доставили оперативно. Слали по два письма в день о том, что с моим заказом и где он едет.
Какие преимущества читалки перед настольной книгой вообще? Я выделяю следующие:
-
Удобство. Для чтения бумажной книги нужен стол, свет, чистые руки. Для меня все три сразу – как парад планет. С читалкой можно бухнуться на диван или залипнуть в очереди в Сбербанке.
-
Таскать бумажную книгу накладно. Лишний вес, книга теряет товарный вид от трения в сумке. Или таскаешь весь день, а шанс почитать так и не выпал.
-
С ценой на зарубежные книги еще можно смириться. Но доставка в два месяца, равная цене книги… нет, я не миллионер, платить 100 долларов за книгу в мягкой обложке.
-
Кое-что достать в бумажном виде уже не реально.
Теперь преимущества конкретно Киндла:
-
Очень высокое качество сборки. Ничто не скрипит, пластик пригнан, щелей нет. Обложка, хоть и не оригинал, тоже на уровне. Магнитик на обложке погружает книгу в сон, если закрыть.
-
Девайс действительно создан для комфортного чтения. Очень четкий экран, ощущение, что читаешь с бумаги. Глубокий контраст, равномерная подсветка. Специально подобранные шрифты.
-
В прошивке только то, что нужно. Нет тяжелого наследия Андроида. Интерфейс минимальный, настройки простые.
-
В поставке идут словари. Читаешь книжку на английском, например, нажал на слово – всплывают две плашки. В одной – выдержка из Оксфордского словаря, в другой – поиск в Википедии. В словаре не просто перевод, а описание термина в разных контекстах.
-
Амазон дает емейл
username@kindle.com
. Шлешь на него книгу – и через минуту она скачалась в читалку. Не надо заморачиваться с проводами. -
Книжки хранятся в облаке. При утрате двайса все легко восстановится. Удалил случайно книжку – всегда можно закачать обратно из облака.
-
Качественная интеграция с сервисом Амазона. Покупаешь прямо в интерфейсе читалки. Все элементы адаптированы под специфичный экран.
Конечно, есть и минусы.
-
Девайс обновляется сам по себе, когда Амазон сочтет нужным. Отключить обновление прошивки нельзя, либо опция запрятана так, что найти не реально. При этом не все изменения мне по душе. Например, в последнем апдейте, чтобы отрегулировать подсветку, теперь нужно три клика, а раньше два. А ведь это самое важное при чтении.
-
Читалка не поддерживает популярные форматы ebup и fb2. Не такой уж минус, полно утилит для конвертации. Но немного раздражает.
-
Я прекрасно понимаю, что устройство полностью контролируется Амазоном. Информация о моих книгах используется, чтобы показывать мне рекламу. Пользоваться Киндлом я смогу ровно столько, сколько позволит Амазон. Столлман прав.
Следует победить юношеские комплексы и брать читалку не по размеру, а качеству экрана. Сейчас самый крутой – 6’ 300 dpi Carta. Он стоит в последних Киндлах и некоторых Ониксах, например, в модели Дарвин. Однако, в Дарвине щели такие, что ноготь пролазит, а по цене почти одинаково.
-
-
Map как замена циклу
Я нашел, что отказ от циклов в пользу
map
улучшает код сразу по нескольким критериям, а заодно вправляет мозги. Код с мапами короче, меньше подвержен ошибкам, его легче поддерживать.Функция
map
родом из мира функционального программирования (далее ФП). Почти любой язык имеет ее аналог в стандартной поставке. Map может быть как функцией, так и методом коллекции.Map принимает функцию и коллекцию. Функция обрабатывает один элемент. Результат
map
– коллекция результатов функции на множестве входных данных.Map призван заменить циклы и ручную итерацию с накоплением результатов. ФП в российских вузах либо не преподают, либо касаются факультативно. Студенты мыслят циклами и переносят привычки во взрослую жизнь.
Мышление циклами – плохая штука. Цикл – примитивная, не регламентированная конструкция, которая быстро выходит из-под контроля и превращает код в лапшу.
Map
лучше цикла по следующим пунктам.Меньше кода
Пусть определена функция
do_stuff(item)
, ее тело не важно. Это штучный обработчик объекта. Сравним два фрагмента кода:results = map(do_stuff, my_data_list)
и
results = [] for x in my_data_list: res = do_stuff(x) results.append(res)
Очевидно, первый вариант короче и не засоряет пространство лишними переменными. Строку с промежуточным результатом
res
можно опустить, но тогда следующая строкаresults.append(do_stuff(x))
станет сложнее. Две операции на одну строку – не желательно.Сдвиг кода и логические прыжки
Цикл – это дополнительный сдвиг кода. ООП уже дает сдвиги для классов и методов, а тут еще циклы. При этом нужно бегать глазами к переменным-спискам, объявленным до цикла. Map не делает сдвига и создает переменные по требованию.
Декларативный код безопасней
Map
– это декларативная операция. Это значит, я говорю что именно хочу получить, а не как. За счет автоматизации ручных действий, декларативный код очень легко поддерживать и в нем меньше багов. Бывает, баг кроется именно в неверной итерации или ручной сборке списка. Но из-за кажущейся тривиальности проверяешь это место в последнюю очередь.Дополнительный уровень абстракции
Map несет большой потенциал для построения абстракций. Скажем, в обычном языке
map
– это банальный прогон функции по элементам в цикле. Но в других языкахmap
обретает преимущества, недоступные обычным циклам.Если у нас неизменяемые коллекции и транзакционная память, интерпретатор раскидает задачу по ядрам. Имея 1000 элементов и 4 ядра, получим по 250 итераций на одно ядро. Описать это циклом – нетривиальная задача. Насколько я знаю, Clojure и некоторые реализации Haskell параллелят
map
при заданных флагах компиляции.Map
очень круто параллелит запросы в сеть. Например, нужно дернуть 100 урлов, при этом сервера находятся в разных полушариях, и время отклика ожидаемо велико. Отклик в 10 секунд на запрос из Европы в Сингапур – нормальное дело. Важно понять, что хоть у вас супер-кластер, он будет висеть 10 секунд в цикле, ожидая блокирующий запрос. По меркам машинного времени это целая вечность.Map
легко перестоит одну схему исполнения в другую. Вот обычный подход, аналогичный циклу:url_list = ["http://foo1.com", "http://foo2.com", ...] def get_data(url): return requests.get(url, timeout=5).json() data_list = map(get_data, url_list)
Теперь напишем свой map с использованием тредов:
from multiprocessing.dummy import Pool def map_threaded(func, *seq): pool = Pool(processes=16) return pool.map(func, *seq) data_list = map_threaded(get_data, url_list)
Всего лишь поменяли
map
наmap_threaded
, но какая большая разница в реализации! Был цикл, а стали треды, но сигнатуры одинаковые. Теперь на всю операцию понадобится время, равное самому долгому запросу.(Да, это пул тредов не смотря на название модуля.)
Мощь абстракции
map
очень велика. Мы можем использовать любой другой метод распределения задач. Например, процессы вместо тредов. Или подключить фреймворк вродеCelery
, который шлет задания воркерам, собирает ответы и отдает список.Мощь Гугла – это комбинация функций Map и Reduce. Инженеры Гугла дробят задачи на подзадачи и эффективно исполняют их на тысячах серверов. Технология работает в масштабах планеты. Под капотом может быть скучный ООП-код на Java, но
Map
иReduce
– абстракция высочайшего порядка. Благодаря им Гугл стал тем, что есть сейчас.Контроль за разрастанием цикла
Проблема цикла в том, что код внутри никак не регламентирован. Тело цикла бесконтрольно разрастается. Сперва кажется, что в цикле все ясно. Но меняются бизнес-требования, нужны фичи, и ясность быстро уходит.
Добавили
if
, затем еще один, затемcontinue
по условию, а потом вообще залепили вложенный цикл. В этой ветке пишем в результирующий список, а в этой нет. Логику накопления результатов отследить трудно.Бывает, мы добавляем костыли в цикл, потому что на носу релиз, и возиться некогда. Часто в ревью мы видим пару строчек:
for item in items: + if not item.check_foo(): + continue ...
которые ни у кого не вызывают подозрений. Ну, пропускаешь шаг, все понятно.
Это плохо.
Ближайшая аналогия – растение морской желудь. Он разрастается по дну судна. Если планово не счищать, скорость судна и потребление горючего падают и возрастают соответственно на 40%.
Отрефакторить цикл, в котором полно
if
,continue
,try/catch
очень трудно. Все завязано на глобальные списки, объявленные до входа в цикл. Операторcontinue
я считаю дурным тоном. Мы должны отделять котлеты от мух до входа в итерацию.Map
исключает условия и пропуск элементов. Целевая функция не может сказать “этот обработать не могу, дай-ка лучше следующий”. Список следует предварительно очистить функциейfilter
.Неверное прохождение слоев бизнес-логики
Мы часто работаем со списками объектов. Типичная задача – извлечь данные из базы и выполнить для каждого ряд действий. Например,
действие1
,действие2
идействие3
.Итерируясь вручную, программист выполняет все три для первого объекта, затем для второго, и так далее. И забывает, что противоположный обход – сначала для всех выполнить
действие1
, затем для всехдействие2
и т.д. – выгодней по следующим причинам:- для очередного действия может потребоваться какой-то ресурс: файл, сеть, авторизация. Логично открыть его, прогнать объекты, закрыть ресурс и переходить к следующей стадии. В цикле придется держать ресурсы открытыми все время.
- если мы лажанулись уже в начале, открывать некоторые ресурсы не понадобиться вообще.
- описанный подход – это проход бизнес-логики по слоям, что ближе к реальному положению дел. Например, вы прошли два действия, но третье не вышло – нет сети или файла. Можно сдампить результаты в файл и несложным хаком вернуться в состояние, когда результаты двух первых стадий есть, а третьей – нет. В случае ошибки на середине цикла контекст будет намного сложней, а вернуться на конкретный шаг итерации – невозможно.
Повышенные требования к коду
Map
заставляет писать код качественней.Map
не имеет встроенной защиты от не пойманных исключений. Если упадет первый элемент, не выполнятся остальные 999. Поэтому передавая функцию в мап, программист должен подписаться, что функция не подведет.Не каждый готов дать эту гарантию. Часто мы видим такое:
result = [] for item in items: try: data = process_item(item) res = process_data(data) result.append(res) except Exception as e: logger.except(e) continue
Этот код ужасен. Он беззуб и неуклюж. Функции написаны криво и бросают ошибки. Смысловая часть уехала на два отступа. На выходе из цикла не ясно, с какими элементами случилась ошибка. Смешались в кучу итерация, отлов исключений, логирование.
А ведь достаточно завернуть целевую функцию в декоратор или другую функцию, которая отдаст пару
(err, result)
и прогнать черезmap
. После прогона отделить хорошие результаты от плохих. Ошибки залогировать отдельным шагом. Хорошие результаты отправить в следующий слой бизнес-логики.Хорошая новость в том, что мы уже в двух шагах от монад, но пока не будем о них.
Вместо заключения
Отказ от циклов сокращает код, делает его яснее и легче в поддержке. Map не позволит воткнуть в середине условие или переход к следующей итерации. Ручное накопление списка чревато багами, поэтому лучше доверить это надежной функции.
-
Записи докладов с четвертой встречи любителей рефакторить
Провели четвертую встречу!
Роман Гребенников выступил с докладом о распределенных системах:
Евгений Рыжков рассказал о тонкостях манипуляций:
Напомню, сообщество тусит в группе Фейсбука. Скоро анонс пятой встречи, принимаем заявки на доклады.
-
Идиотизм вокруг NPM
Как и другие разработчики, я с интересом наблюдаю за скандалом вокруг экосистемы Node.js и пакетного менеджера NPM.
Если вы не в курсе дела, смотрите ссылки в конце поста. А я напомню, что у Node-разработчиков прошлая неделя прошла в боли и страданиях. Программист Азер Кочулу удалил из NPM свои 100500 пакетов, том числе один аж на 11 строк. Без него не смогли собраться React, Babel и куча всего.
Азер удалил код потому, что администрация NPM в одностороннем порядке передала права на пакет Kik от Азера к представителям одноименной компании. Якобы юристы Kik запугали патентами и торговой маркой.
Развязка: Азер перенес все пакеты в Гитхаб, администрация NPM запретила удалять пакеты по истечении 24 часов в момента публикации. Азер на коне, он весь такой Д’Артаньян в белых перчатках, в Твиттере пафосные картинки.
Страсти спадают, и теперь, после недели хаоса, следует подумать, кто в это ситуации был мудаком.
Имеем три стороны – Азер, администрация NPM и юристы компании Kik. Кто неправ в этой истории?
Первые два. Объясню с конца.
Юристы Kik действуют в своих интересах. Они стараются расширить экспансию вверенного им продукта. Совершенно естественно, что их цель – захватить имя Kik разных его проявлениях.
Поскольку под именем Kik размещен левый пакет, а компания хочет предоставить разработчикам API, юристы начали переговоры о передаче прав на пакет.
Азер обозвал их мудаками и послал на хуй (как было, так и перевел). А в начале написал “Ха-ха”. Кто-то сомневается в неадекватности пациента? Взрослый же человек, просто откажи, зачем оскорблять людей?
Увидев, что собеседник в неадеквате, юристы пишут администрации NPM. Я бы тоже так поступил на их месте. Тут NPM жидко сливаются, передав права без каких-либо документов и согласований. Да, пусть этот Азер хамло, но права пользователей надо уважать.
Кто-то думает, что NPM зассали перед патентом на торговую марку “Kik”, а на самом деле все просто – одни из основателей NPM работал в Kik раньше. Это банальный блат, работает не только в России.
Поэтому администрация NPM – тоже мудаки.
Далее Айзер включил бунтаря и удалил все 250 пакетов, что успел к тому времени наплодить. Многие из них успели стать зависимостями в других пакетах. Айзер, прекрасно осознавая что делает, испортил жизнь тысячам людей по всему земному шару.
Когда валютные вкладчики приковывают себя наручниками к дверям банка, то и это не тот градус шизофрении, что ученил Азер. Это детская нужда стать нужным. Послать миру весть, что без него нельзя обойтись.
Администрация NPM, к их чести, вынесла правильный урок. Теперь запрещено удалять пакеты, с момента публикаций которых прошло больше 24 часов. Это правильно, потому что если разработчик выложил код, то значит, он предлагает его другим, верно? Иначе зачем выкладывать, ставь из приватного репозитория.
Не хочешь поддерживать – ставишь галку, что ищешь мейнтейнера, передаешь права. Удалять – это нонсенс. Что будет, если каждый разработчик, поругавшись в подружкой, начнет удалять модули? Это строительство дома из пивных банок.
Давайте окинем быстрым взглядом 250 пакетов Азера. Двести пятьдесят, Карл, как пишут в интернете.
- run-paralelly – 11 комитов, 46 строк.
- left-pad, из-за которого сломался интернет. 11 комитов, 11 значимых строк. 660 звезд.
- flat-glob – 6 комитов, 49 строк. Прогресс.
- get-object-path – 9 комитов, 21 строка.
- rnd – 1 комит, 8 строк.
- random-color – 7 комитов, 8 строк.
- route-map – 6 комитов, 55 строк.
Ну ты понел.
В интернете точно пошутили, что чуваку нужен не Гитхаб, а Твиттер. Каждый твит – новый пакет. Большая часть уместится в 140 символов, если переменные укоротить. А “тяжелые” проекты на 50 строк и больше декомпозировать и подключить зависимостями.
Блин, у него декларация
package.json
занимает больше места, чем код.Что мешает этому Азеру создать пакет
azer-tools
и наполнять его утилитами? Принцип “один пакет – одна функция” это не глупость, a рак мозга. Я не видел такого ни в одном другом языке.Ничего удивительного, что подобное произошло. Странно, как такое до сих пор не случилось, учитывая идиотизм разработчиков вроде Азера. Когда ты строишь дом из пивных банок, всегда найдется тот, кто выдернет самую нижнюю, потому что на дне еще осталось. И все упадет.
А ведь я помню поток статей в духе “Node.js спасет мир” пять лет назад. 2016 на дворе, а в сообществе сканалы на уровне Дома-2.
На закуску – статья Теда Дзюбы “Node.Js Is Cancer”. На Хабре был перевод.
Ссылки:
-
Что значит код как данные
Про Лисп говорят, что код это данные, а данные – код. Пока не поработаешь с Лиспом, понять смысл кода как данных трудно. Ниже – моя попытка объяснить простыми словами.
Предположим, кто-то осваивает Лисп и делает задачки вроде факториала и рекурсии. Частенько приходится записывать числовые выражения вроде
(x + y) * (a / b)
. В Лиспе пишут польской нотацией, то есть выражение выглядит так:(* (+ x y) (/ a b))
Не очень привычно. Было бы здорово, думает студент, написать функцию, которая принимает выражение как удобно мне, а возвращала то, что нужно Лиспу. Начинает писать функцию
expr
, что-то вроде(expr (x + y) * (a / b))
.Ничто не предвещает беды. Но вот облом: Лисп, как и другие ЯП, вычисляет аргументы функции до входа в нее. А выражение
(x + y)...
ошибочно с точки зрения и семантики, и синтаксиса, поэтому даже до вызова функцииexpr
дело не дойдет. Что же делать?Помогут макросы.
Макрос похож на функцию, но с важным отличием. Выражение, переданное в макрос, НЕ вычисляется. Вместо этого макрос получает список лексем. Задача макроса – перестоить список в правильную Лисп-форму и вернуть ее, НЕ вычисляя. Интерпретатор сам вычислит ее, получив из макроса.
Короткими словами, макросу можно скормить полную дичь: крестики-нолики, математическое выражение, кусок кода на любом языке, asii-арт. Внутри макроса это станет списком. Задача макроса – перестроить дичь в правильный список, который вычислит Лисп.
Именно в этот момент срабатывает срабатывает принцип код-как-данные. Когда мы пишем
(expr (x + y) * (a / b))
, для нас это код. Но внутри макроса это список! Нулевой элемент списка – открывающая скобочка, первый – символ x, затем символ плюсика, и так до последней закрывающей скобочки.Рассмотрим макрос для вычисления выражений из двух операндов. Он вычислит простейшие вещи вроде
3 + 2
,2 * 10
,-3 / 2
:(defmacro expr [v1 op v2] `(~op ~v1 ~v2)) (expr 2 + 2) >>> 4 (expr 2 / 2) >>> 1
Видно что в макросе
expr
я поменял элементы списка местами. Выражения длиней трех лексем макрос не примет. Для проивольного выражения понадобятся разбор и рекурсивный спуск, что выходит за рамки разговора.Вот почему Лисп – программируемый язык программирования. Любой участок кода может быть обработан как список, что делает язык невероятно мощным. Лисп разрешает любой недостаток своими же средствами. ООП-системы, средства обработки исключений и многие другие фундаментальные вещи – всего лишь пакеты с макросами.
PS: Кроме Лиспа, я знаю только один язык со схожим поведенем. Это Tcl (Тикль) – в котором, грубо говоря, все является строкой. Тикль до сих пор популярен на производстве: заводах, CAD-системах. Нефтяные платформы Shell работают под управлением системы, написанной на Тикле.
-
О техзадании
Когда программист на первой же встрече просит техзадание, знайте, что это плохой программист. То есть, он может быть хорошим техническим специалистом, но плохим исполнителем. Скорей всего, он сделает не то, что нужно было заказчику. Сотрудничество окажется непродуктивным.
Теперь длинное объяснение.
Что такое ТЗ? Это документ, в котором бизнес-требования описаны техническим языком. Упоминаются протоколы, стандарты, меры безопасности. Четко описаны процессы обмена данными.
Когда программист просит ТЗ, он не понимает, что требует невозможного. У заказчика не может быть правильно составленного технического документа. Все, что у него есть – это обрывки идей будущего бизнеса.
Задача программиста – помочь оформить бизнес-идеи в техническую форму. Заказчик не может сделать это сам. Он не знает разницу между
HTTP
иHTTPS
,GET
иPOST
, и чем стандартная авторизация отличается отOAuth 2.0
.Если программист отказывается составлять ТЗ, это значит, он не разработчик, а кодер, фрилансер, которому можно доверить только самую примитивную работу.
Заказчик с готовым ТЗ это редкость. Если есть готовое ТЗ, скорее всего, над ним уже работал технический специалист. Зачем тогда уходить к другому? Или найти помощника через того, кто составлял ТЗ.
Поэтому первая фаза встречи программиста и заказчика – это много-много вопросов. Исполнитель имеет право брать за это деньги, если гарантирует, что на выходе получится ТЗ, максимально полно отражающее бизнес-требования.
Программисты-невежды думают, что заказчику составить ТЗ будет легко. Предлагаю обмен ролями: пусть заказчик напишет ТЗ, а программист – бизнес-план проекта на будущий год. Затем каждая сторона оценит результаты другой.
Описанное выше можно изобразить на графике. Вот как протекает сотрудничество во времени. Программист думает:
|ТЗ |код |код |код |код |код |готово >------------------------------------------------------------------------> время
На самом деле:
|вопросы |вопросы |вопросы |ТЗ |код |код |код |готово >------------------------------------------------------------------------> время
Когда основные вопросы решены и ТЗ составлено, настает время решать, стоит ли работать над проектом. Возможно, начальные идеи выродятся во что-то другое.
А вот что будет, если следовать первому графику.
ТЗ – это документ. Однако, напрасно программист думает, что ТЗ как золотой щит спасет от всех разногласий. Помимо проекта, который описан в ТЗ, есть еще одна его версия, которая живет в голове заказчика.
В идеале они совпадают, и второй график учит, как этого достичь. Неудовлетворенные ожидания – это очень плохо, и даже при самом строгом ТЗ заказчик найдет способ или разорвать контракт, или не выплатить сумму целиком, или еще что-то.
- Заказчик: Форма авторизации как-то не очень…
- Программист: Все сделано по техзаданию.
- Заказчик: Да, но нет авторизации через соцсети.
- Программист: Этого не было в ТЗ.
- Заказчик: Не было, потому что сейчас это везде, я думал, вы догадаетесь.
- Программист: Вы не отразили это техзадании.
- Заказчик: Что вы все с техзаданием! У нас будет меньше пользователей!
- Программист (мысленно): Вот мудак, не знает, что хочет.
- Заказчик (думает): Послал же бог дурака. Только по бумажке работает, мозга ноль.
И так до следующей встречи. С новым заказчиком и исполнителем. Драмы и плач на форумах.
Выводы
Хороший исполнитель не берется за проект пока досконально не изучит предмет. Он долго задает вопросы. Техническое задание фиксирует итог совместных исследований. ТЗ максимально точно соответствует ожиданиям заказчика. Документ не поможет, если реализация не оправдала надежд.
-
Деструктивный синтаксис в функциях
С удивлением обнаружил, что в Питоне редко пользуются деструктивным синтаксисом в сигнатурах функций. Рассмотрим, какие преимущества он дает.
Деструктивный синтаксис пришел, как все лучшее, из мира функционального программирования. В Питоне он известен как “распаковка кортежа” на составные переменные.
Распаковывать можно не только кортеж, но и списки и вообще все итерируемые коллекции. Правда, для множеств и словарей не определен порядок итерации, что может привести к багам.
Простой пример:
point = (1, 2) x, y = point
Данные часто собираются в группы. Например, точка – это два числа, которые удобно хранить и передавать вместе. Результат функции тоже может быть парой – флаг успеха и результат. В кортежи удобно собирать права доступа, табличные данные.
Некоторые программисты теряются на этом месте и пишут классы, чтобы группировать данные. Переход к классам уводит нас в противоположную сторону от коллекций и всех их преимуществ. Лучше хранить данные в коллекциях насколько это возможно.
Например, работаем с точками. Есть две точки, нужно вычислить декартово расстояние. Решение в лоб:
point1 = (1, 2) point2 = (3, 4) def distance(p1, p2): x1, y1 = p1 x2, y2 = p2 ... print distance(p1, p2)
Хорошо, но лишние строки на распаковку. Решение в ООП-стиле:
class Point(object): def __init__(self, x, y): self.x = x self.y = y def distance(self, other): # use self.x, self.y, other.x, other.y # to refer variables ... p1 = Point(1, 2) p2 = Point(3, 4) print p1.distance(p2)
Лишние строки на класс. К тому же, я забыл добавить метод
__str__
и во время дебага увижу что-то вроде<object Point at 0x523fw523>
вместо чисел. Матерясь, полезу дописывать метод и только увеличу энтропию.Самая простая, и потому лучшая реализация:
point1 = (1, 2) point2 = (3, 4) def distance((x1, y1), (x2, y2)): ... print distance(p1, p2)
Видим, что распаковка происходит на уровне сигнатуры. Это значит, в теле функции уже доступны компоненты точек и остается только посчитать значение.
Такая функция лучше защищена от неправильных аргументов. Если передать кортеж не с двумя, а тремя компонентами, ошибка распаковки случится раньше, еще до входа в функцию.
Деструктивный синтаксис следует принципу “явное лучше неявного”. Он избавляет от долгих описаний вроде “аргумент
permission
– это кортеж, где первый элемент то, второй се, третий…”. Сигнатура с распаковкой скажет сама за себя.Распаковывать кортежи можно и в лямбдах. Я пользуюсь этим для обработки словарей, когда нужна пара ключ-значение:
data = {'foo': 1, 'bar': 2, 'baz': 3} process = lambda (key, val): 'key: %s, value: %d' % (key, val) print map(process, data.iteritems()) >>> ['key: baz, value: 3', 'key: foo, value: 1', 'key: bar, value: 2']
Деструктивный синтаксис сокращает код, делает его декларативным, а значит, удобным в сопровождении.