webidl icon indicating copy to clipboard operation
webidl copied to clipboard

Specify best practices for sequence/FrozenArray/ObservableArray

Open tabatkins opened this issue 7 months ago • 4 comments

What is the issue with the Web IDL Standard?

There's a lot of legacy APIs that were created before ObservableArray was added, so we have a panoply of behaviors across the web. It's very difficult for spec authors to know which of the three types they should use for representing some array value.

I think the basic summary of best practices is:

  1. If you want to expose an array, and the values won't change from author or UA action, use a read-only FrozenArray.
  2. If you want to expose an array, and the values are changeable from author action (and possibly from UA action), use an ObservableArray.
  3. If you want to expose an array, and the values are only changeable from UA action, choose between a read-only FrozenArray (especially if authors will want to poll the values frequently) and a method that returns a sequence. Consider exposing a promise-returning method to let authors wait for updates, if changes are infrequent, so they don't have to poll. (Frequently-changing values are fine to poll for, usually.)

Notably, I think best practice should warn against using a mutable FrozenArray; this was used in the past to allow replacing the value wholesale (there's even an example of that exact behavior in the FrozenArray section), but if you allow replacing all of the items, you should allow replacing individual items as well, and just use an ObservableArray.

Thoughts?

tabatkins avatar Jun 13 '25 22:06 tabatkins

I think this is the current state of things:

  1. If you want to expose an array, and the values won't change from author or UA action, use a readonly FrozenArray attribute.
  2. If you want to expose an array whose values can only change from UA action, use a readonly ObservableArray attribute with throwing "set an indexed value" and "delete an indexed value" algorithms
  3. If you want to expose an array whose values can change from author (and possibly UA) action, use a non-readonly ObservableArray attribute with appropriately-validating "set an indexed value" and "delete an indexed value" algorithms
  4. If you want to get a snapshot of values at a given time, use a method that returns a sequence. Note that this is the only suitable choice if your values are dictionaries, because dictionaries are inherently snapshots.

Historical notes and subtleties:

  • The only author-facing difference between a FrozenArray and an ObservableArray-with-throwing-algorithms is whether Object.isFrozen(a) returns true or false. It's not really clear whether this is important; if we abandon this, then we could eliminate (1) in favor of lumping it in with (2).
  • Historically people have used FrozenArray attributes for case (2) and (3) because ObservableArray didn't exist. Nobody seems to want to take the time to convert.
  • We vaguely envisioned introducing a ReadonlyArray type for case (2) as a specification authoring device. That is part of why all the examples in the ObservableArray section are for case (3). This has kind of stalled, but wouldn't be too hard to do, so I'm unsure whether to give up on it and encourage ObservableArray, or just keep hoping that someday someone will make the time.
  • Apart from that, the specification-writing experience for ObservableArray is, IMO, very good. The "backing list", "set an indexed value", and "delete an indexed value" concepts are easy to work with, and the way in which they only work with attributes simplifies things greatly. In contrast, the specification-writing experience for FrozenArray is not great, as it's trying to pretend to be a generic type, and spec authors have to do a lot of work manually. https://github.com/whatwg/webidl/issues/810#issuecomment-538651524 is a relatively-recent take on how this could be improved, and https://github.com/whatwg/webidl/pull/1413 laid some of the groundwork.
  • None of these technologies work for if you want to add methods to your array (e.g., an item() method). (The feature request is #1342)

domenic avatar Jun 16 '25 05:06 domenic

I'm still in favor of attempting to eliminate 1 in favor of 2 and making 2 easier for specification authors. Both of these also seem like fairly straightforward refactoring projects that are a good way for someone to get familiar with the standardization and implementation process.

annevk avatar Jun 16 '25 06:06 annevk

@domenic Thank you, that's a much better summary! I agree with all of this.

@annevk Would you prefer holding it as a Good First Project, then? Would you mind if I at least PR'd some informative text additions/changes, recommending the above? The spec currently gives "use a mutable FrozenArray to allow authors to mutate things" as an affirmative example, so it's actively misleading spec authors right now.

tabatkins avatar Jun 16 '25 18:06 tabatkins

No, anyone is welcome to solve it! Have at it. 😊

annevk avatar Jun 17 '25 06:06 annevk