On Wed, Oct 22, 2025, at 10:30 AM, Edmond Dantes wrote:
>> This isn't even the same example. We're not talking about type juggling, but an
>> interface. Mixed is not an object nor an interface.
> Why?
> Type and Interface are contracts.
>
>> The "FutureLike" type is exactly what I'm arguing for!
>
> I have nothing against this interface. My point is different:
> 1. Should the await and awaitXX functions accept only Future?
> 2. Should Awaitable be hidden from the PHP userland?
>
>> foreach($next = await($awaitable)) { }
> What’s the problem here? The object will return a result.
> If the result is iterable, it will go into the foreach loop. An
> absolutely normal situation.
>
>> error: multiple usages of multi-shot Awaitable; you may get a different result on each
>> invocation of await()
> Why can’t you call await twice? What’s illegal about it?
> If it’s a Future, you’ll get the previous result; if it’s an
> Awaitable, you’ll get the second value.
> But the correctness of the code here 100% depends on the programmer’s intent.
And that's exactly the problem. The correctness cannot be inferred without asking the
programmer.
There is, obviously, no such thing as idiot-proof code, as the world will always create a better
idiot. But we should still strive to be idiot-resistant. There are two general ways of doing that:
1. Affordances - Basically, it should be hard to use wrong.
2. Construction - It should be impossible to use wrong in the first place. (Ie, a compile error.)
The classic example is US electrical plugs vs EU plugs. They *should* be different shapes, because
they're different voltages. If you plug an unsuspecting device into the wrong voltage, it goes
boom. So the different plug interfaces make it patently obvious that you're doing something
wrong because they don't fit together (construction), and if you use an adapter that is your
signal that you need to worry about voltage conversion (affordance).
Using the same type for something that can be read only once vs something that should be read
multiple times is equivalent to using the same plug for both 120v and 240v current. (Or USB-C using
the same plug for 8 different speeds, 5 different power delivery levels, and sometimes no data at
all. It sucks.) The developer needs to "just know" which one is intended, because the
code doesn't tell them. That's a problem.
Instead, like Rob said, there should be a one-shot object that dies as soon as it is successfully
read. That is guaranteed to be single-use, and reading it multiple times is detectable by a static
analysis tool as "you clearly are doing this wrong, no question."
Then a multi-read object is... an Iterable that produces those single shot objects. I'm not
sure if that even needs its own object that implements Iterable, or if it's OK to not define it
and just say it's any iterable<Awaitable>. (So, a generator would work just as well.)
I'm leaning toward the latter, but there may be uses for the former, not sure.
But having a "dual voltage" awaitable object is exactly the sort of footgun many of us are
talking about. The contract is under-specified, so it's too easy for the developer to be
"holding it wrong" and plug it into the wrong wall socket. Boom.
--Larry Garfield