Open azjezz opened 3 years ago
/cc @veewee
Would it make sense to make the data structures immutable?
That way the questions are answered with:
Not sure if it makes sense from a consumer pov though.
I've also researched some other languages: https://rosettacode.org/wiki/Priority_queue
Most use something like while(!empty)...
or while (0 !== count)
to iterate instead of making it an iterator.
Some languages support both iterating and the while approach.
I think it would be nice if you could use the functions inside the Iter\
namespace on the data structures.
I like the idea of immutable data structures.
If i understand correctly:
$queue = new Queue();
$queue = $queue->enqueue('hello');
$other = $queue->enqueue('hey');
$queue->count(); // 1 ['hello']
$other->count(); // 2 ['hello', 'hey']
$queue->dequeue(); // 'hello'
$queue->count(); // 0 []
$other->count(); // 2 ['hello', 'hey']
maybe we can do the same as in Collection
, and have Queue
, PriorityQueue
, Stack
be immutable, with another variant MutableQueue
, MutablePriorityQueue
, and MutableStack
that are mutable ( see Psl\Collection\Vector
vs Psl\Collection\MutableVector
) ?
The dequeue
method in your example should also be immutable then? Meaning the while will need to reassign the parameter pointing to the new version of the queue as well (which might be a bit counterintuitive).
Having both options is nice, but also more work to create and maintain. They should behave in a similar way, meaning that the 3 questions on top need to be answered in the same way for both implementations.
If I understand the haskell priority queue well, it also works in an immutable way. (But I am an absolute noob in that language :) )
Here's a real world usage of PriorityQueue ( https://github.com/nuxed/http-server/blob/develop/src/Nuxed/Http/Server/Handler/NextMiddlewareHandler.hack#L5-L26 ):
final class NextMiddlewareHandler implements Server\IHandler {
/** @var SplPriorityQueue<Server\IMiddleware> $queue */
private SplPriorityQueue $queue;
private Server\IHandler $handler;
public function __construct(SplPriorityQueue $queue, Server\IHandler $handler) {
$this->queue = clone $queue;
$this->handler = $handler;
}
public function handle(Message\IServerRequest $request): Message\IResponse
{
if (0 === $this->queue->count()) {
return await $this->handler->handle($request);
}
$middleware = $this->queue->dequeue();
return $middleware->process($request, $this);
}
}
if we switch to immutable DS, this is how the API would look like ( correct me if i'm wrong ):
final class NextMiddlewareHandler implements Server\IHandler {
/** @var SplPriorityQueue<Server\IMiddleware> $queue */
private SplPriorityQueue $queue;
private Server\IHandler $handler;
public function __construct(SplPriorityQueue $queue, Server\IHandler $handler) {
$this->queue = clone $queue;
$this->handler = $handler;
}
public function handle(Message\IServerRequest $request): Message\IResponse
{
if (0 === $this->queue->count()) {
return await $this->handler->handle($request);
}
[$queue, $middleware] = $this->queue->dequeue();
$this->queue = $queue;
return $middleware->process($request, $this);
}
}
the return type of dequeue
is now bothering me, i'm not sure if immutable would be a good idea here since most PHP software seems to be using it in a mutable context.
I think tupples are getting more and more known in the community. The immutable implementation is indeed more verbose. Therefore, I suggest following things:
Next on the topic of iterations:
examples:
// Iterables
$items = map([...$queue], ($value) => 'something');
$items = filter([...$queue], ($event) => $event instanceof SomeEvent);
// ...
// Looping over them with more control:
// We could add an isEmpty - which makes sense
while ($queue->count()) {
$value = $queue->dequeue();
}
// More advanced -> see your NextMiddlewareHandler
For your specific use-case a mutable version might make more sense. Even though I don't fully grasp the idea behind the NextMiddleware. It does something different each time it is handled, but in a regular server middleware stack - it only goes through the handle once?
For immutable it could look like this:
public function dispatch($event)
{
$queue = $this->queue;
while ($queue->count()) {
[$queue, $subscriber] = $queue->dequeue();
$subscriber($event);
}
return $event;
}
Which could also be used with iterator functions to make it less verbose.
It would be really nice to have a fallback with an optional mapping to https://github.com/php-ds.
see discussion in #53 ( https://github.com/azjezz/psl/pull/53#discussion_r500772765 )
Questions: