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

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

Вау! Это замечательно. Функциональное программирование на PHP это круто ) А ещё круто, что ваше решение очень компактно и синтаксически вкусно. Есть конечно, небольшая проблема с тем, что юзаются функции, но думаю в 90% это не вызовет проблем.
Функций там всего несколько, и они служат обёрткой на Builder'ами:

function to_collection($data)
{
    $builder = new CollectionBuilder();

    return $builder->addAll($data)->build();
}

Т.е. можно использовать Builder'ы сами по себе, не касаясь функций.

Эту часть в статье я как раз не раскрыл, т.к. подумал, что будет слишком для одного раза :)
Если кого то беспокоят функции, то их можно вынести в фабричные методы, например. По логике как раз подходит, раз уж все равно в них только создание объекта. Но с функциями запись короче, я бы сделал и тот и тот вариант, на любителя :)
Вот честно… Вроде как смысл таких библиотек — упрощение синтаксиса работы с коллекциями.
Но, ИМХО, обычный for/foreach гораздо читабельнее, чем приведенные выше конструкции.
Возможно это дело привычки, но прочитанный код мне кажется совсем неочевидным…
Вот тоже, периодически пытаюсь использовать array_filter или array_walk, но возникает вопрос, а foreach будет ли не нагляднее и не проще ли для понимания?
Кстати, да, забыл сказать, что в том же PHP есть вполне нативный мапперы (array_map, array_walk, array_walk_recursive), фильтры (array_filter) и еще немеряно всяких вкусностей для массивов, реализованных на низком уровне и работающих быстро.
Хочется такого себе синтаксического сахара? Да вот, пожалуйста. Всё есть.

Но и опять же, останавливает то, что прочитать такой код с первого раза тяжелее, чем обычный foreach.

Вообще, в php так много функций обработок массивов и итераторов, как в наборе array_, так и в SPL, что городить еще что-то… Ну не вижу смsсла в упор.
Нет, используйте array_filter когда нужно убрать ненужные элементы из массива или оставить только нужные, и используйте array_walk когда нужно изменить элементы данного массива, а foreach для итерирования нужен, а не для filter/reduce/etc. операций
Мне это утверждение видится несколько голословным.
Вы не могли бы привести хотя бы пару доводов в обоснование своей позиции?

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

Лично я для себя нашел только одно удобное применение array_filter() — проверка на наличие непустых ячеек в в массиве:
if (!array_filter($arr)) {
  echo "Все ячейки пустые!"
}
Тут всего один довод — вы используете существующие функции по назначению. Я не знаю, насколько это будет правильно, но можно это назвать «семантично».

> Во-первых, как уже здесь упоминалось, foreach нагляден, привычен и читабелен.
foreach ($array as $k => $el) {
    if ($el['smth'] > LIMIT) {
        unset($array[$k]);
    }
}

куда читабельней чем array_filter(), а привычки это дело наживное.

> Во-вторых, foreach позволяет совершенно естественно реализовать логику фильтрации любой сложности. В то время как для array_filter в любом мало-мальски сложном случае придется писать отдельную функцию.
Для этого в 5.3 ввели анонимные ф-ии, вот как выше приведенная логика будет выглядеть с array_filter:
$array = array_filter($array, function($el) {
    return $el['smth'] <= LIMIT;
});
Ой. В пыхе столько функций, что если использовать все — код писать будет некогда.
Есть реально удобные — http_build_query(), например.
Но когда функцию можно заменить простым циклом — я не вижу смысла вводить в код новую сущность.

Я лучше сам цикл заверну в функцию, и буду обращаться к ней. Это получится чище и понятнее, чем ваш анонимный уродец. Вы сами-то не видите разве, какой франкенштейн получился?

Я прекрасно понимаю программиста, которому хочется написать покороче. Но я, к сожалению, имею опыт не только написания, но и чтения кода. И я дам 10 тупых но читабельных копи-паст методов против одного завернутого хака с переподвыподвертом, экономящего аж две строчки кода.
То что я хотел объяснить — когда я вижу array_filter — я понимаю, что тут мы отсекаем ненужные элементы, когда я вижу foreach — я понимаю только то, что мы будем итерироваться.
Да и раз уж программируете на этом языке — то стоит знать что он может и имеет. Да, тут 5к ф-ий, но для работы с массивами их не больше 30 и думаю о них стоит знать.

А то что тут написано… ну это логика велосипедостроения, вперед и с песней.
C array_filter я ещё могу согласиться, что это более очевидно, чем foreach, но что делать с array_walk, array_map и т.д.?

На данный момент я использую лишь array_filter, там где это возможно(и то осторожно), а вот все остальное…
А мне вот array_map() нравится. Он тоже очень очевиден.
Например в таких случаях (сферический пример в вакууме):

$tags = explode(',', $_POST['tags']);
$tags = array_map('trim', $tags);
Класс — спасибо.
Такое использование действительно читается достаточно быстро, и главное оно меньше и элегантнее чем foreach!
$method = function($name) {
    return function($item) use($name) { return $item->$name(); };
};

$itemsTitles = array_map($method('getTitle'), $items);
$itemsTexts  = array_map($method('getText'), $items);
$itemsDates  = array_map($method('getDate'), $items);


vs.

$itemsTitles = array();
foreach ($items as $item) {
    $itemsTitles[] = $item->getTitle();
}
$itemsTexts = array();
foreach ($items as $item) {
    $itemsTexts[] = $item->getText();
}
$itemsDates = array();
foreach ($items as $item) {
    $itemsDates[] = $item->getDate();
}
vs.

$items = to_collection($items);

$itemsTitles = $items->mapBy(x()->getTitle());
$itemsTexts  = $items->mapBy(x()->getText());
$itemsDates  = $items->mapBy(x()->getDate());
Мне совсем не нравится эта бессмысленная фунция x(). Если уж ты начал делать DSL, основанный на функциях, то чего бы не идти до конца:

use collada\to_collection;
use collada\method;

$items = array_to_coll($items);

$itemsTitles = $items->mapBy(method('getTitle'));
$itemsTexts  = $items->mapBy(method('getText'));
$itemsDates  = $items->mapBy(method('getDate'));
Увы, вообще не читабельно.
Не знаю, не знаю.
Я бы не сказал, что это прочитать проще, чем foreach, к тому же второй пример должен выглядеть вот так, что согласитесь, для понимания гораздо легче:
$itemsTitles = array();
$itemsTexts = array();
$itemsDates = array();
foreach ($items as $item) {
    $itemsTitles[] = $item->getTitle();
    $itemsTexts[] = $item->getText();
    $itemsDates[] = $item->getDate();
}

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

$itemsTitles = array_map($method('getTitle'), $items);
// $itemsTitles = ARRAY OF $items, MAPPED BY method getTitle




$itemsTitles = array();
foreach ($items as $item) {
    $itemsTitles[] = $item->getTitle();
}
// $itemsTitles = ARRAY
// for each $item in ARRAY OF $items
// append to $itemsTitles $item->getTitle()
Во-втором случае мы используем строго возможности языка. Их ограниченное кол-во и их поведение мы знаем точно, так что на осмысление конструкции уйдет 1 «такт».

В-первом случае, мы зависим от некого $method, о реализации которого, мы пока ничего не знаем. Т.е. нам придется туда погрузиться — 2 неких «такта»

Если все разработчики точно знают что такое $method, соглашение используется во всем проекте, то возможно это и хорошо, в других случаях это усложнение со всеми вытекающими.
Ну вы только что подтвердили мои слова. Вы мыслите о коде в плоскости «тактов», я мыслю в плоскости «что я хочу получить». Весь смысл конструкций в функциональном стиле как раз в том, что вам не нужно видеть реализации, чтобы понять что делает $method('getTitle'). Вот серьезно, не притворяйтесь что можете прочитать эту конструкцию как-то по-другому нежели «метод getTitle».
Я не притворяюсь. У нас есть команда.
Есть периодические смены состава. Бизнес трактует правило жизни проекта.
Необходимо как можно быстрее вводить в строй программистов, что бы они с наименьшим кол-вом временя для адаптации входили в рабочий режим.

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

Чем больше подобных $method появится, тем тяжелее сопровождать проект. А главное, тяжелее определиться объяснить команде, когда использовать foreach, а когда $method.
Извиняюсь за некоторые выражения(«Я не притворяюсь»), я сам не понял, как они попали в текст.
foreach позволяет совершенно естественно реализовать логику фильтрации любой сложности. В то время как для array_filter в любом мало-мальски сложном случае придется писать отдельную функцию.


Не вижу разницы, между кодом, который вы заключаете в блоке фигурных скобок в foreach и кодом внутри функции для array_filter — и там и там можно реализовать логику фильтрации любой сложности.
Как описать x() в phpdoc (для автокомплита правильных геттеров)?
Никак.
Странно, что в конкурентах не упомянут YaLinqo.
Забыл про эту библиотеку. Добавлю к сравнению.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации