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

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

У вас в библиотеке composer.lock закоммичен

Спасибо, удалил.

А почему composer.lock должен быть не закоммичен? Если его не будет, как тогда composer install выполнить?

Вы случаем не путаете его с composer.json?

Нет, не путаю. И composer.json, и composer.lock должны быть в системе контроля версий. Почитайте, чем команда composer install отличается от composer update.

вы ошибаетесь

composer.lock комитится ТОЛЬКО на финальном проекте, библиотеки не должны его коммитить!

UPD: тоже увидел отбой поздно

composer.lock содержит зафиксированные версии пакетов. composer install в первую очередь смотрит на composer.lock файл. Если его нет, тогда решает какие версии пакетов ставить в зависимости от того, что указано в composer.json. composer.lock файл комитят в конечных проектах, чтобы вся команда разработчиков работала с одинаковыми версиями пакетов, но в библиотеках так делать не стоит, чтобы не навязывать конкретные версии пакетов проектам, которые будут пользоваться библиотекой.

  1. А зачем вообще в целом нужны не пустые коллекции, и что будет с не пустой коллекций когда в ней не станет элеметов (clear вызову)?

  2. Типобезопасность - это конечно великолепно что на doc-блоках держится "типа безопасность", но в чем проблема (хотя бы опционально) добавить проверку типа при обновлении коллекции (set, update)?

  3. filterNotNull - если уже делать по аналогии с имеющимися функциями, то логично было бы сделать логику array_filter, когда при вызове без callback все пустые (да не только null) элементы удаляются

  4. Ковариантность - а что простите в доктрине не так? Объявите такой же докблок и норм, нет?

  5. Иммутабельность - старый добрый clone видимо уже не катит? А то что на каждом вызове будет новая коллекция память засирать, это норм?

  6. HashMap - очередной неоправданный велосипед - SplObjectStorage

1) У непустых коллекций отличается сигнатура некоторых методов. Например, возвразаемый тип head и reduce не содержит ни null, ни Option т.к. непустая коллекция гарантирует, что хотя бы один элемент в коллекции присутствует. Непустые коллекции позволяют пользоваться такими операциями без каких-либо проверок на null. clear - это по сути тот же filter(fn() => false). В статье есть пример, где после операции filter NonEmpty префикс коллекции пропадает из-за того, что коллекция может стать пустой.

3) Такое поведение array_filter лично мне кажется не особо явным

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

5) Увеличенный расход по памяти конечно стоит учитывать при работе с иммутабельными коллекциями. Но в первую очередь библиотека пропагандирует функциональный подход, а иммутабельность - это один из главных столпов.

6) С помощью SplObjectStorage не получится чейнить операции и там нельзя хранить скалярные типы. Так же, HashMap используется внутри HashSet.

Дисклеймер: не, в целом норм, так держать, лучше делать, чем не делать, лови плюсик и вот это всё :)
Но.

5) Увеличенный расход по памяти конечно стоит учитывать при работе с иммутабельными коллекциями. Но в первую очередь библиотека пропагандирует функциональный подход, а иммутабельность - это один из главных столпов.

$a = [];

for ($i=0; $i<1_000_000; $i++){
    $a[] = [$i, "$i"];
}
var_dump(memory_get_usage());
$map = \Fp\Collections\HashMap::collect($a);
unset($a);
var_dump(memory_get_usage());
$map->map(fn($value, $key) => $value+2)
    ->map(fn($value, $key) => $value/2)
    ->map(fn($value, $key) => $value + 1)
    ->map(fn($value, $key) => $value + 1)
    ->filter(fn($value, $key) => $value % 2 === 0);

В map добавил тот-же var_dump(memory_get_usage());
Результат.

int(441961296)
int(802031336)
int(1603587320)

Fatal error: Allowed memory size of 2147483648 bytes exhausted

Рили?


Второй момент, который лично меня сильно огорчил - это документирование и оригинальный подход к порядку параметров.

callable - нууу, ок. Зайду внутрь, посмотрю, что за callable тут ожидается.

ээээ... ШТА!? value вперёд key?

Для каждой операции над коллекцией написана дока в соответствующем интерфейсе с суффиксом Ops. Например LinkedList -> Seq -> SeqOps.

Там есть короткие примеры использования в формате REPL.

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

Что касается порядка аргументов, то чаще всего в операциях типа map и фильтр используется именно значение. Ключ можно использовать, но это редкий кейс.

Такой порядок аргументов позволяет в вашем примере написать вот так и не указывать вообще ключ как второй аргумент.

$map->map(fn($value) => $value + 2)
    ->map(fn($value) => $value / 2)
    ->map(fn($value) => $value + 1)
    ->map(fn($value) => $value + 1)
    ->filter(fn($value) => $value % 2 === 0);

то чаще всего в операциях типа map и фильтр используется именно значение.

Для Map-ов обычно используется Entry, у которой можно достать ключ и значение по необходимости, а если используется деструкция, то сохраняется семантика коллекции ключ->значение

Поменял на Entry в 3.0.0. Рассматривал ещё вариант с массивом, но из-за того, что деструктуризация не работает как например в foreach ($pairs as [$key, $value]), то сделал Entry класс, объекты которого живут только в рамках одной итерации.

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

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

Только язык активно уходит от срока жизни в один запрос - многие библиотеки/каркасы уже пишутся держа в уме Swoole, AmPHP, RoadRunner, Workerman или PHP-PM.

Интересный проект. Надо будет оценить производительность. Я в свое время сравнивал nikic/iter (построенной на генераторах) с cakephp/collection (построенной на итераторах) и результат был в пользу последней.

Проект, в целом, показался мне сильно продвинутой функциональной версией того, что не хватает в cakephp/collection.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации