I mean ... you understand I work with this stuff daily and yet you think you can explain to me how it works ... but thanks for the example though, let's see why I insist pyodide can't
<!DOCTYPE html>
<head>
<script src="https://cdn.jsdelivr.net/pyodide/v0.23.4/full/pyodide.js"></script>
<script>
globalThis.Arrow = Object.defineProperty(
() => {},
'new',
{value() { return 'arrow' }}
);
console.log('JS', Arrow.new());
// it logs "JS arrow"
</script>
<script type="module">
let pyodide = await loadPyodide();
pyodide.runPython(`
import js
print(js.Arrow.new())
# pyodide.ffi.JsException: TypeError:
# () => {} is not a constructor
`);
</script>
</head>
</html>
Pyodide is inevitably guessing that a proxied function that access a new field is meant to construct an instance but above example shows that guessing leads to errors otherwise not present in the JS world.
Not at all. If the new was standardized at the Function.prototype level, pyodide wouldn't need to guess anything, it would just use the Proxy traps as they are.
# get trap on global context
js.Arrow
# get trap on Arrow function
js.Arrow.new
# apply trap on Arrow.new with
# Arrow as context + optional arguments
js.Arrow.new()
- If
newthere is own property, everything would work as expected. - If
newthere is inherited from Function, it will throw like it would in JS because arrow can't be constructed. - If
newthere is inherited andArrowis actually a class, it would construct the resulting object passing through theconstructProxy trap, which already holds the Arrow class.
Right now, because nobody is understanding what's the benefit of this proposal or is even needed, all interpreters need to guess or find workarounds, or use try/catch, or ... while if new was standard:
- nobody would likely override it unless really desired
- every interpreter would stop guessing around and just use Proxy traps without re-inventing the wheel
- no ambiguity would exist unless meant to be ... that's the case of explicit
Class['new'] = ...
It's demonstrated in this post they can't guess anything like that ... they can't pretend anything: we need a standard to rely on and less guessing to avoid issues like the one showed in here.
Back to the topic: it would still be possible from the Python side of affair to explicitly constructs via Reflect.construct(JSClass, [...args]) but that would be either for internal use cases or for the 0.1% of real-world scenarios out there where nobody is adding static new out of the blue and, when they do, it's because that's needed.
I hope now it's clear what pyodide or any other interpreter can do, and what they can't due lack of Symbol.constructable ... and again, if that symbol was backed into JS, none of this conversation would have ever happened because at that point the apply proxy trap would've had simply brand-checked classes (as in ES6+ classes) VS any other non-class function JS can offer.
If this proposal goes through, and until standardization, pyodide and all others have plenty of time to adjust their logic accordingly which, in the long term, would make pyodide and others more robust, not less, and it will guide any future player to do the right thing and use what's standard behavior in JS too.
That being said, my poly is not needed in Pyodide or MicroPython (WASM) but for what I could test including the poly ASAP as polyscript bundle, everything still works as expected, no regression in there.