Hello, Rob!

 It is probably best to have channels be non-buffered by default (like in
> Go); buffered channels can hide architectural issues in async code.
>
Great point! If a channel has a default capacity greater than 1, it leads
to implicit behavior that the user must be aware of. If something results
in implicit behavior, it's a bad practice. I'll fix it!

> Why do we need "finishing" producing and consuming? Won't "closing" the
channel be enough, or does closing a channel prevent consumers from reading
pending items?

Because additional methods allow us to express explicit semantics while
also performing extra checks. Although, in essence, it's the same as the
close() method.
I'm not 100% sure whether this implementation is the right choice because
it has more elements compared to Go. However, the close() method has an
ambiguity regarding whether we want to terminate the Consumer or the
Producer. And ambiguity leads to errors.

> Personally, it should be an exception (or at least a warning) if a
channel gets GC'd with pending data.

Yes, we need to think about what would be better—an exception or a warning.

The GC considers whether the channel was explicitly closed or not.
However, if someone tries to close consumption while there is still data, a
warning is mandatory because this is, once again, *explicit* semantics. If
a programmer wants to close a channel with data, they must first call
discardData() and only then close it.

So yes, explicit operations are much better.

> Looking at the public api, it seems that it is very hard to use
correctly. It would probably be a good idea to pair it down and come up
with an api/ideomatic usage that makes it hard to use incorrectly.

  What exactly is difficult to use?

> Can we name this Async\Closure or something similar? Just to keep it
inline with the current \Closure

  It's hard for me to judge this. On one hand, the name *Callback* clearly
reflects what this object does.

  > Speaking of, you probably meant to use \Closure as the $callback
property instead of mixed?

  Unfortunately, PHP does not support the *callable* type for properties.

  > - The constructor for a Future is public, but it looks like it would be
weird to use.

I'm currently working on this module and decided to base it on the
implementation in AMPHP. So, the Future class will just be a wrapper around
FutureState. And yes, its constructor can remain public, allowing Future to
be used as DeferredFuture.

 > It would be nice to see this simply use Futures as much as possible. For
example, returning a Future instead of FiberHandles.

  Yes, this will be implemented a bit later, after the *Future* module is
fully ready. Most likely, this week.

  >  Is onSignal OS signals or application signals? (I didn't look at the C
code) If the latter, it is probably better to use something other than
integer. If the former, this is usually handled by extensions (such as
pcntl) and comes with a ton of caveats. It might be better to let those
extensions handle that.

Yes, these are OS signals, but they are essentially cross-platform. Can int
be replaced with something else, like an enum? Of course.

> - Exposing $reserved -- which is accessible via reflection -- is probably
a bad idea.

Yes, I’ve thought about this. It really doesn’t seem like the best idea.

I might end up making the Notifier object final, which would eliminate this
issue.

> Should this be implementing Stringable interface? Also, this will
automatically cast to a string if accidentally passed to a function that
takes a string -- unless you are using strict mode. https://3v4l.org/3AqcW This
is probably not desired.

Yes, all Notifier classes have a string representation for analysis
purposes.

Do you think it would be better to use a separate function instead of
__toString to avoid implicit behavior? If it helps prevent unintended
behavior, then you’re probably right.

> Instead of Terminate(), should it be Close(), to be consistent with the
rest of the library?

  Yes, maybe close() is better.

> - It would be nice to get a list of callbacks on the Notifier as well.

I 100% agree. I'll add this method now before I forget.

 > It would be nice to be able to get a future from the Walker instead of
polling "isFinished".
Of course.

>  IMHO, it would be better to implement this around Futures (see amphp's
extensive library around this). Here are some examples from my own code:

  Sorry, but for some reason, I don't see the code. But just in case, I can
say in advance that Walker was essentially made thanks to *AMPHP*—I
borrowed part of the implementation from there :)

  >  timeouts: race two futures, one which times out and the other which
performs a long-running task. If the timeout wins, then I cancel the
long-running task and proceed as an error. If the long-running task wins, I
cancel the timeout.

  Why do you need Walker for this?

  >  - wait for all to complete: there are a couple of variations on this
theme. But sometimes we want to wait for all to complete, errors or not;
sometimes we want to stop on the first error; and sometimes we want to just
get the first success (see timeouts).
  This looks more like AwaitAll() / AwaitAny(). These functions will also
be available once I finish Future.

> $result = match (Async\select($channel)) {
>  Async\EOF => "channel closed",
 > default => $channel->receive(),
>}

Why?

foreach ($channel as $data) {
    // ...
}

just use it :)

> Also, keep in mind this is pretty valid
  That's exactly how it's implemented now. The channel does not treat NULL
as the end of transmission—the programmer must close it explicitly, or the
*GC* will do it.

Thanks for the great comments!  They will help make this product better.

Reply via email to