Pull to refresh

Comments 45

UFO just landed and posted this here
То есть у вас основная претензия — это что автодополнение кода не работает?..
UFO just landed and posted this here
По-моему, вы немного не по адресу. В PHP утиная типизация, какого вывода типов вы от него хотите? А делать вывод типов как в примере выше на уровне IDE — это очень сложное и неблагодарное занятие. Я не знаю сред разработки для динамически типизированных языков, которые умеют подобное вытворять (ну, разве что в частных случаях).
В PHP утиная типизация
С каких пор?
Вы часто видите типы у аргументов функций? А у результатов функций и переменных? :) Или, может, скалярные типы в type hinting уже поддерживаются? Или вообще, в стандартных функциях «mixed» уже не упоминается?

PHP, конечно, пытается мимикрировать под джаву, но — слава богам! — пока не настолько активно.
Действительно, каюсь. Понедельник — день тяжелый.
Вы путаете утиную и динамическую типизацию.
Утиная типизация — вид динамической, так что автор ничего не путает.

<?php
class Duck {
    function quack() { echo "Quack", PHP_EOL; }
    function fly() { echo "Flap, Flap", PHP_EOL; }
}
 
class Person {
    function quack() { echo "I try to imitate a duck quack", PHP_EOL; }
    function fly() { echo "I take an aeroplane", PHP_EOL; }
}
 
function in_the_forest($object) {
    $object->quack();
    $object->fly();
}
 
in_the_forest(new Duck);
in_the_forest(new Person);


Quack
Flap, Flap
I try to imitate a duck quack
I take an aeroplane
Это умеет IDE, не вижу особо проблем с тем, что бы написать простенький плагин для какого phpstorm с добавлением ресолва типов в замыканиях и для автокомплита.
Вы уверены, что плагин будет простеньким? А то уже вечность висит баг WI-3477 про недоступность PHP в списке «внедряемых языков» (фича, которая позволяет определять и анализировать SQL внутри строк, например).
Ну я лично против php внутри строк, тем более что этот тикет никак не повлияет на вашу реализацию. у вас все же синтаксис замыканий не являтся валидным для php. Все относительно конечно, но не вижу особых проблем. А тот тикет — он не нужен никому.
Эти строки можно писать по-разному, один из синтаксисов валиден. Можно и добавить альтернативу, например, заменить ==> на /**/. От реализации этого тикета анализа кода в строке не появится, но хоть будет нормальная подсветка и проверка синтаксиса.

PHP в строках — зло, но это далеко не единственная библиотека, которая этим грешит. Какая-нибудь аннотация типа «этот аргумент — код» определённо не помешала бы.
Выглядит классно, но хотелось бы пару бенчмарков. Сколько времени исполняется ваш пример на тестовом массиве, скажем, из пары тысяч элементов?
Вы можете написать аналог на голом PHP для сравнения?

В моих бенчмарках с относительно простыми запросами оверхед болтается между 20% и 200%, в основном 50%–100% (во сравнению с реализацией на циклах). Чем меньше действий происходит в «теле», тем больше оверхед в процентном отношении (на абсолютно голом цикле можно получить 500%). От использования функций на массивах с колбэками скорость тоже заметно просаживается, к слову. Сколько потеряет конкретное приложение — надо смотреть по приложению.

Если память не изменяет, LINQ в .NET тоже просаживает скорость. Какое-то время назад обещали, что заоптимизировали, но не проверял.
Ну оверхэд за счет вызова функций и прочего естественно будет, тут уж ничего не поделать. Можно попробовать написать аналог чисто на map/reduce/filter/usort что бы было чеснее. Так же стоит включить в бенчмарки статистику по потреблению памяти, просто из любопытства.
Сахар сахаром, но хранить куски кода в строках — брррррр. Ничего не имею против самой либы, но пользоваться ей (если бы пришлось) я бы стал только примерно так:
<?php
$result = from($categories)
    ->orderBy(function($cat){return $cat["name"];})
    ->groupJoin(
        from($products)
            ->where(function($prod){return $prod["quantity"] > 0;})
            ->orderByDescending(function($prod){return $prod["quantity"];})
            ->thenBy(function($prod){return $prod["name"];}),
        function($cat){return $cat["id"];},
        function($prod){return $prod["catId"];},
        function($cat, $prods){return [
            "name"     => $cat["name"],
            "products" => $prods,
        ];}
    );

Громоздко, сложно к восприятию. Но всяко, имхо, лучше строк.
P.S. Если и так часть кода — это простые function($a) { return $a['fieldName']; } то почему бы не заменить на:
<?php
$result = from($categories)
    ->orderBy('name')
    ->groupJoin(
        from($products)
            ->where('quantity', '_ > 0') // это уже фантазии на тему
            ->orderByDescending('quantity')
            ->thenBy('name'),
        ['id', 'catId'],
        function($cat, $prods){return [
            "name"     => $cat["name"],
            "products" => $prods,
        ];}
    );

По сути такимо образом пытались видимо обойти то ограничения, что в PHP нету удобного синтаксиса для лямбд. Да и ваш вариант сложнее воспринимать.
Первый вариант да, он является полным аналогом варианта из поста (если я правильно его понял). Второй же подход мне видится гораздо симпатичнее.
Для сравнения
    ->orderByDescending($prod ==> $prod['quantity'])
    ->thenBy($prod ==> $prod['name']),

и
    ->orderByDescending('quantity')
    ->thenBy('name'),


Уж, думаю, библиотека может выполнить что-то вида
if (is_string($v)) {
    $res = isset($dict[$v]) ? $dict[$v] : null;
}
elseif (is_callable($v)) {
    $res = $v($dict);
}
else ...
Вы оптимизировали синтаксис под частный случай. :) А если в where мне нужно условие и для значения, и для ключа (массивы в PHP содержат и то, и другое)? А если мне нужно отсортировать по синусу количества? А если ключи массива для соединения вложенные, и вообще это не ключи масства, а свойства объекта или результаты вызова функций? А как мне передать ссылку на именованную функцию (подсказка: в PHP указатели на функции трёх типов: строки для функций, массивы из пары объект-строка для методов, массивы из пары строка-строка для статических методов)? И так далее.

В Ginq есть синтаксис для получения полей из Symfony (и нет строковых лямбд), но эта штука просто космически тормозная — оверхед за 1000% и дальше. И эта штука удобна в join'ах и сортировках, но совершенно бесполезна в select и прочих методах.

В принципе, отдельный синтаксис лямбд для получения полей не лишён смысла, но это надо хорошо продумать, с бухты-барахты это не сделаешь. И уж точно нет смысла менять аргументы у функций, потому что от всех вариантов перегрузок тогда можно будет убиться (и сейчас-то непросто, со всеми этими ключами).
Я не знаком с LINQ, оценивал по вами же приведенным примерам :)

>> А если в where мне нужно условие и для значения, и для ключа
where(function($k, $v) {return $v['quantity'] > 0 && ($k%2 == 0);}) 

>>А если мне нужно отсортировать по синусу количества?
orderByDescending('quantity', 'sin(_)')

>>и вообще это… свойства объекта…
__get, ArrayAccess,…

>>А как мне передать ссылку на именованную функцию
use(...)? Или не совсем понял о чем речь.

Я к чему это все. Всякий раз сталкиваясь с «yet another LINQ port», не понимаю, зачем настолько досконально пытаться перенести на тот же PHP то, что целиком и полностью завязано на возможностях синтаксиса C#. Почему не продумать альтернативу при похожем подходе с цепочками вызовом?
Сейчас вы начинаете строить в уме трижды вложенные циклы, вызовы функций для массивов

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

2. Ведь данные хранятся в БД, соотвественно нужные данные в нужном формате можно выбрать SQL запросом, что убдет явно быстрее чем их обработка на PHP.

3. Интересно было бы посмотреть сравнение быстродействия и используемой памяти:
— Использование SQL запроса;
— Обратботка сущесвующих массивов с помощью YaLinqo (хотя сюда нужно суммировать и выборку данных из БД, потому что вряд ли вы данные храниет в массивах);
— То же самое что и предыдущий вариант, только вместо библиотеки — реализация нанативном пхп под определенную задачу.
А при вызове функций библиотеки сколько вложенных циклов будет? К примеру, в вашем примере какая сложность алгоритма получается?

Математическая сложность алгоритма не меняется. Меняется «логическая» сложность алгоритма. Например, цепочка select-where-selectMany представляется линейной цепочкой вызовов, и человек воспринимает это как простую последовательность трёх операций: преобразовать, отфильтровать, преобразовать со слиянием. Если же разворачивать цепочку в код, то будут вложенные for-if-for-for, которые сильно напрягут мозг и заставят анализировать кучу мусора. join за собой скрывает for-if-for, некоторые больше, некоторые меньше. Последовательные операции в разы проще воспринимать, чем вложенные. Сортировка orderBy-thenBy — проще, чем usort и колбэк с функцией сравнения (кода в три раза больше, распознавать порядок ещё сложнее).

Ведь данные хранятся в БД, соотвественно нужные данные в нужном формате можно выбрать SQL запросом, что убдет явно быстрее чем их обработка на PHP.

Не все данные хранятся в БД. Вы б второй абзац прочитали что ли.

По поводу быстродействия см. комментарии выше.
По поводу «логической» сложности полностью согласен. А по поводу математической — нет. Ведь использование дополнительной функции, библиотеки, фреймворка — явно увеличивает сложность. Только что я не много потыкал под xhprof. Вот код с использованием либы, а вот нативный. И результаты:
Нативный:

С либой:

Видно, что с использованием либы — памяти раз в 5 больше и скорость выполнения — раз в 8 и количество вызываемых функциий — раз в 10. Если решать задачу с использованием SQL запроса — то еще быстрее.
Об этом я и спрашивал.

Я не спорю, что нативный код не всегда хорош (не учитывая хайлоад приложения), и подход который предлагаете Вы- явно удобнее и логически приятнее читать, особенно если бы не было лямбда функций в строках, но использование дополнительных библиотек — это всегда жертвование ресурсов, и здесь важно знать, чем ты жертвуешь и в каком количестве.
Идеальный комментарий, который я искал, открывая топик =)
Хм. Расход памяти какой-то конский. Там не должно выделяться столько. Надо глянуть.

А по поводу математической — нет.

Мера О та же самая. Коэффициенты и константы, разумеется, отличаются.

а вот нативный

1. Вы неправильно отсортировали категории: отсортировали по и так возрастающему ключу, хотя требовалось по имени. То есть не отсортировали вообще.
2. Вы неправильно отсортировали продукты: отсортировали только по количеству, хотя надо было по двум полям, в том числе по имени.

Собственно, к вопросу о наглядности кода. :)

Если решать задачу с использованием SQL запроса — то еще быстрее.

Если у вас есть база с гигабайтами данных, то использование этой библиотеки — ошибка по определению. Эта библиотека предназначена для случаев, когда SQL нет. Вот вы от веб-сервиса получили сотню записей — вы будете запихивать их в базу, чтобы применить SQL?

Кроме того, что сравнение с SQL идейно некорректно (вы ещё с C++ сравните), оно неадекватно и по другой причине: в реальном коде гораздо больше шансы увидеть толстые ORMы, а не голый SQL. На ORMы все плюются, но пользуются. Здесь примерно та же история.

не учитывая хайлоад приложения

PHP и хайлоад в одном предложении — признак недальновидности.
Да, точно. Времени не было толком, что бы сделать все.
Вот с сортировками правильными (хотя может и тут ошибки есть):



Разница с предыдущим вариантом использования нативного — не велика.

Мера О та же самая. Коэффициенты и константы, разумеется, отличаются.

Я почемуто уверен, что вложенность будет выше, соотвестно степень N будет возростать.

Для решения на нативном коде — потребуется больше времени, хотя есть такая мудрость — «Нету смысла подключать дополнительный пакет компонентов в 100500 строк, чтобы твой исходник уменьшился на 10 строк».

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

Вот с сортировками правильными

Теперь сортировка по коду первого символа в нижнем регистре. о_О Почему не strcmp?

Ладно, не суть важно. Погонял у себя (правда без xhprof) — соотношение примерно то же, только раз в пять быстрее.

1. На строковых лямбдах можно сэкономить (эвал — он и в Африке эвал).
2. Погонял туда-сюда числа продуктов и категорий — соотношение скорости между голым PHP и YaLinqo сохраняется, так что остаюсь убеждённым про тот же порядок сложности.
3. Вызовы функций в PHP таки космически дорогие…
2. Погонял туда-сюда числа продуктов и категорий — соотношение скорости между голым PHP и YaLinqo сохраняется, так что остаюсь убеждённым про тот же порядок сложности.

Очень круто, когда есть график, когда по X — размер данных, по Y — время :) Особенно если на графике показано несколько реализаций решения.
Не спорю, круто, но мне лень. :) Если уж на то пошло, то я и на PHP-то последнее время не программирую. Так, решил обновить библиотеку, чтобы не протухла. Всё-таки мой единственный опенсорсный проект, который живыми людьми используется (если packagist не врёт).
>> Если уж на то пошло, то я и на PHP-то последнее время не программирую
C# на замену?
PHP и хайлоад в одном предложении — признак недальновидности.

Тоже спорно. Это уже как стереотип — ПХП плохой, подходит только для бюджетных не больших проектов.
С правильным подходом и хорошей архитектурой приложения — мощный инструмент, с помощью которого вполне можно и хайлоад приложения разрабатывать.
Просто слово «хайлоад» — резиновое. Нужно более конкретно говорить :)

P.S. На пхп писать «говнокод» в разы легче и порог вхождения низкий, в отличии от других языков, по этому так и бывает, что когда видишь уже готовый проект — начинается паника :)
А что там за проблема с лямбдами? Давно уже стараюсь на PHP не писать, так что просто не в курсе. Строки с кодом выглядят не приятно.
Был предложен патч который позволял избавиться от «function () {… } » и использовать более короткую запись, но мейнтейнеры посчитали что это слишком сложно и потому не нужно (тоже самое было и с полноценными геттерами и сеттерами аналогами из C#, там вообще было две реализации предложено). Вообще иногда кажется что они специально развивают язык в том направлении чтобы им могли пользоваться абсолютные идиоты, кладя болт на реальные проблемы (я всё удивляюсь как генераторы пропихнули)…
C# для меня сейчас вообще единственная причина того, что у меня win — основная ОС. Да, да, есть Mono, но VS+R# слишком уж сладкая парочка.
«сложным» не в контексте «разработчики не разберутся как это использовать» а сложно поддерживать. Вообще сложность состоит в убогом парсере, от которого уже который год хотят избавиться (этот же чувак ответственен за то что в PHP есть генераторы), но пока есть другие приоритеты видимо. Вот они управление памятью перепилили, глядишь и парсером займутся.
Т.е. если от убого парсера всё-таки избавятся можно надеяться и на нормальные лямбы и на геттеры? Хорошо бы…
Вроде как решили пока не торопиться и оставить старый добрый single-pass парсер с кастылями и хаками. Во всяком случае после 2012-ого я ничего нового по этой теме не слышал.
сложность состоит в убогом парсере

Что касается парсера, то меня убивают «фичи»:

1. Ура! Теперь можно писать (new Foo)->baz()!
2. Ура! Теперь можно писать foo()['baz']!

В каждой новой версии одно и то же…
Изза убогости парсера такие вещи не так то легко сделать. Вроде как только недавно добавили пулреквест реализующий почти все (а то и все) варианты разыменования.
Если приглядеться, в коде ещё кое-что можно усмотреть, но лучше так не делать.
Sign up to leave a comment.

Articles