Why can't parts of the spec be written in JavaScript itself?

Take Object.fromEntries for example:

If the spec would specify it as:

Calling Object.fromEntries behaves as calling the following function (causes the same side-effects and returns the same value / throws the same exceptions for all possible aguments, as if it would be run in a seperate ECMAScript Agent):

function fromEntries(iterable) { const result = {}; for(const [key, value] of iterable) result[key] = value; return result; }

As far as I can see, this would result in the same accurate specification (for sure this is a simplified example, I've never written any specification so this is probably far from perfect, but I hope it's enough to get the idea).

If parts of the spec would be written that way, it would:

(1) Make the spec far more readable, as most people writing JS are not used to read formal languages, but they are obviously used to read JS.

(2) Provide accurate polyfills.

(3) Would simplify proposals, and would allow more people to write proposals.

I'm probably not the first one with that idea, and there are probably some good reasons why that isn't done. So my question is: What am I missing?

Note that that code isn't exactly the same as what's currently specified - you need something more or less the same as https://github.com/es-shims/Object.fromEntries/blob/511f215e413d877ad17faaed69985c85deb06792/implementation.js to do that (including that const [key, value] of iterable invokes the iterable protocol on iterable's results, too, instead of doing a [[Get]] on the 0 and 1 properties).

However, your question still remains - why couldn't we specify the method with JS such that it was correct?

I don't know all the historical reasons why it's done this way, as opposed to with code - but one answer is that the reliable means don't necessarily exist.

One example: result[key] = value uses [[Set]] semantics, so it would invoke setters installed on Object.prototype - which means we'd need to use Object.defineProperty, but there's no reliable means to get at the original/primordial/builtin function value (since someone could have redefined it on Object). So, lacking any syntax for [[Define]], and lacking an unforgeable means to get original function values, I don't think it'd be possible to specify Object.fromEntries reliably in terms of JS code.

1 Like

As Jordan points out, various internal operations would need to be exposed in unforgeable ways (syntax, typically) which currently don’t exist. In some cases these things could not be exposed without significant changes, or perhaps not at all. For example, many intrinsic functions need to access slots that associate internal state with ES objects. These operations need to work across instances from different realms, but JS provides no mechanism for sharing unforgeable private names across realms and probably never could. Also, many operations need to handle ‘values’ that that have no reified form in JS, like references and completions, or access other forms of state that cannot normally be observed at all because it is associated with execution rather than a JS object, like how ArraySpeciesCreate (used by various methods, e.g. Array.prototype.map) references internal state associated with ‘the current realm’.

Some existing operations probably could be specified in JS alone. For example I think we could implement Math.min in JS because we can get access to ToNumber syntactically through + UnaryExpression and the ToPrimitive calls in ‘Abstract Relational Comparison’ are redundant (in this context) and Type can be written in JS. However I suspect most would not find it especially desirable for a handful of algorithms to be specified differently from the rest based on a fairly arbitrary property (that they can be) and other algorithms that can’t be will still need to reference some of the same shared operations ... meaning you’d end up specifying the same ops two different ways.

I could be wrong about this, but my sense has been that the primary audience for which the specification is written, if there is one, is implementors. Those implementors could be working in any language — which includes JS, e.g. for polyfills, but even a polyfill implementor will need to become familiar with certain internal concepts and will often benefit from the explicit clarity of the prose text. Teachers and enthusiasts in the community tend to provide the documentation that’s consumed by most JS devs, e.g. via MDN.

Regarding proposals, it’s totally cool to draft proposals without specification text. In fact I suspect a polyfill implementation is of greater value at that stage. Typically the spec text will be written further down the line by the proposal’s champions and gets refined as the proposal progresses.

Thanks for answering.

You do have a point with "the primary audience". From my point of view (developer / StackOverflow answerer) writing the spec partly in JS itself would make it more readable for me, but I'm probably part of a minority that reads the spec directly, you are right that most people will look up things on MDN first (and if something isn't explained there, they'll ask on SO). The implementors, as you said, probably benefit from the way the spec is currently written, and thats the main point here (which I missed).

That said I'm glad to hear that drafts can be written in JS itself, that might encourage me to write one somewhen :)

Regards, Jonas

1 Like

Don't get me wrong, if you managed to write large swaths of the spec in JS itself, that'd be interesting - I created https://npmjs.com/es-abstract to implement many of the spec's abstract operations in JS.

2 Likes