Good day, Larry.

> First off, as others have said, thank you for a thorough and detailed
proposal.
Thanks!

> * A series of free-standing functions.
> * That only work if the scheduler is active.
> * The scheduler being active is a run-once global flag.
> * So code that uses those functions is only useful based on a global
state not present in that function.
> * And a host of other seemingly low-level objects that have a myriad of
methods on them that do, um, stuff.
> * Oh, and a lot of static methods, too, instead of free-standing
functions.

Suppose these shortcomings don’t exist, and we have implemented the boldest
scenario imaginable. We introduce Structured Concurrency, remove low-level
elements, and possibly even get rid of Future. Of course, there are no
functions like startScheduler or anything like that.

   1. In this case, how should PHP handle Fiber and all the behavior
   associated with it? Should Fiber be declared deprecated and removed from
   the language? What should the flow be?
   2. What should be done with I/O functions? Should they remain blocking,
   with a separate API provided as an extension?
   3. Would it be possible to convince the maintainers of XDEBUG and other
   extensions to rewrite their code to support the new model? ( *If you're
   reading this question now, please share your opinion.* )
   4. If transparent concurrency is introduced for I/O in point 2, what
   should be done with Revolt + AMPHP? This would break their code. Should
   an additional function or option be introduced to switch PHP into "legacy
   mode"?

I share your feelings on many points, but I would like to see some
real-world alternative.

>
> I commend to your attention this post about a Python async library
>

Structured concurrency is a great thing. However, I’d like to avoid
changing the language syntax and make something closer to Go’s semantics.
I’ll think about it and add this idea to my TODO.

> async $context {
> // $context is an object of AsyncContext, and can be passed around as
such.
> // It is the *only* way to span anything async, or interact with the
async controls.
> // If a function doesn't take an AsyncContext param, it cannot control
async.  This is good.

This is a very elegant solution. Theoretically.

However, in practice, if you require explicitly passing the context to all
functions, it leads to the following consequences:

   1. The semantics of all functions increase by one additional
parameter (*Signature
   bloat*).
   2. If an asynchronous call needs to be added to a function, and other
   functions depend on it, then the semantics of all dependent functions must
   be changed as well.

In strict languages, a hybrid model is often used, or like in Go, where the
context is passed explicitly as a synchronization object, but only when
necessary.

In this example, there is another aspect: the fact that async execution is
explicitly limited to a specific scope. This is essentially the same as
startScheduler, and it is one of the options I was considering.

Of course, startScheduler can be replaced with a construction like
async(function()
{ ... }).
This means that async execution is only active within the closure, and
coroutines can only be created inside that closure.

This is one of the semantic solutions that allows removing startScheduler,
but at the implementation level, it is exactly the same.

What do you think about this?

> I'm not convinced that sticking arbitrary key/value pairs into the
Context object is wise;

Why not?

> that's global state by another name

  Static variables inside a function are also global state. Are you against
static variables?

> But if we must, the above would handle all the inheritance and override
stuff quite naturally. Possibly with:

 How will a context with open string keys help preserve service data that
the service doesn't want to expose to anyone? The Key() solution is
essentially the same as Symbol in JS, which is used for the same purpose.
Of course, we could add a coroutine static $var construct to the language
syntax. But it's all the same just syntactic sugar that would require more
code to support.

> [$in, $out] = Channel::create($buffer_size);

This semantics require the programmer to remember that two variables
actually point to the same object. If a function has multiple channels,
this makes the code quite verbose. Additionally, such channels are
inconvenient to store in lists because their structure becomes more complex.

I would suggest a slightly different solution:

<code php>
$in = new Channel()->getProducer();
async myFunction($in->getConsumer());
<code>

This semantics do not restrict the programmer in usage patterns while still
allowing interaction with the channel through a well-defined contract.

Thanks for the great examples, and a special thanks for the article.
I also like the definition of context.

Ed

Reply via email to