Bug 14878 - Rename const to legacyconst
Rename const to legacyconst
Status: RESOLVED WONTFIX
Product: WebAppsWG
Classification: Unclassified
Component: WebIDL
unspecified
All All
: P2 enhancement
: ---
Assigned To: Cameron McCormack
public-webapps-bugzilla
:
Depends on:
Blocks:
  Show dependency treegraph
 
Reported: 2011-11-18 19:08 UTC by Aryeh Gregor
Modified: 2011-12-27 14:22 UTC (History)
7 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Aryeh Gregor 2011-11-18 19:08:19 UTC
Anne thinks const should be renamed to legacyconst, like legacycaller, to discourage spec writers from introducing more numeric constants.  Numeric constants are an antipattern that we want to avoid in new interfaces.  You always want to use string literals instead.
Comment 1 Cameron McCormack 2011-11-19 08:27:54 UTC
I think there can be legitimate reasons for wanting to use const, for things other than numeric codes.  Mathematical constants for example.  So I don't think we should rename const to legacyconst.
Comment 2 Anne 2011-11-21 10:14:28 UTC
Anything besides Math? Math is handled by ECMAScript. So far all the API we know have introduced bad usage of const.
Comment 3 Travis Leithead [MSFT] 2011-11-21 17:39:09 UTC
(In reply to comment #0)
> You always want to use string literals instead.

Is this referring to string literals assigned to const properties, or are you simply saying that const was a bad idea, and in general shouldn't be used anymore in future APIs?

There are certainly some advantages to having numerical constants, especially for APIs that can have overlapping state (like compareDocumentPosition, or mouseevent.buttons) and use a number as a bit mask.

I think this "problem" might be better solved by some WebIDL recommended best practices for const, rather than flagging all const properties as "legacy" (do not use).
Comment 4 Anne 2011-11-21 19:33:48 UTC
Const was a bad idea. Developers do not like bitmask APIs.
Comment 5 Brendan Eich 2011-11-21 19:37:38 UTC
(In reply to comment #4)
> Const was a bad idea. Developers do not like bitmask APIs.

Evidence?

It's a bit extreme, since JS has |, &, and ^, and it's hard to beat those if you have a small enough set (union, intersection, difference).

Is there a usability problem that JS developers don't face, that is inflicted here by some combo of WebIDL and JS?

/be
Comment 6 Anne 2011-11-21 19:43:55 UTC
http://ejohn.org/blog/comparing-document-position/ "This, undoubtedly, makes for the single most confusing method of the DOM API" about compareDocumentPosition()'s bitmask behavior.
Comment 7 Anne 2011-11-21 19:49:24 UTC
I agree it's a bit extreme though and I'm sure there's people that like bitmasks. I'm just do not think we should have them at the API-level.
Comment 8 Brendan Eich 2011-11-21 19:51:27 UTC
(In reply to comment #6)
> http://ejohn.org/blog/comparing-document-position/ "This, undoubtedly, makes
> for the single most confusing method of the DOM API" about
> compareDocumentPosition()'s bitmask behavior.

Isn't the problem there all the magic number hardcodings? Where are the manifest constants naming the bits?

(Did I see && used where & was needed too, and possibly some precedence problems to boot? Ok, C's operator hierarchy is awkward. I won't cry if you ban numeric consts, but I side with Travis: we need best-practices, not outright bans.)

/be
Comment 9 Aryeh Gregor 2011-11-22 01:47:48 UTC
(In reply to comment #3)
> (In reply to comment #0)
> > You always want to use string literals instead.
> 
> Is this referring to string literals assigned to const properties, or are you
> simply saying that const was a bad idea, and in general shouldn't be used
> anymore in future APIs?

Specifically, a number of APIs use numeric constants instead of string literals.  This is the right way to do things in C, but it's wrong for JavaScript.  It's much longer to type, because you need to qualify everything -- like Node.ELEMENT_NODE instead of "element".  On the other hand, in JS there are no advantages.  In C people do it because they want to optimize their data structures and using a char** or such when you could use a char is insane, but in JS it's much more natural to just use strings or objects or such.

> There are certainly some advantages to having numerical constants, especially
> for APIs that can have overlapping state (like compareDocumentPosition, or
> mouseevent.buttons) and use a number as a bit mask.

Bit arithmetic is unfamiliar to the average casual scripter, although of course professional programmers should be familiar with it.  It would be much easier for authors if compareDocumentPosition returned a dictionary:

  if (node1.compareDocumentPosition(node2) & Node.DOCUMENT_POSITION_PRECEDING)
vs.
  if (node1.compareDocumentPosition(node2).preceding)

(Although really .precedes() would be best; we already have contains().)

MouseEvent.buttons would be better as a dictionary too.  Again, it's the difference between e.buttons.right vs. e.buttons & 2 (DOM 3 Events apparently doesn't even define numeric constants here? o_O).

> I think this "problem" might be better solved by some WebIDL recommended best
> practices for const, rather than flagging all const properties as "legacy" (do
> not use).

That's only the case if there are any cases people can come up with where we should actually be using const.  If not, renaming to legacyconst is a good idea to hammer home the point.  We have too many new APIs that are making the same mistakes as old ones -- we should really be learning from them, and WebIDL is one of the few things that web spec writers are almost certainly going to have to use.
Comment 10 Yehuda Katz 2011-11-22 05:10:19 UTC
Bitmasks are not a good user-facing API for JavaScript. Anecdotally, SproutCore 1.x used bitmasks for some state management code in our data store API, and it was one of the most confusing (and "weird") parts of that API.

Is there anything lost by simply using String literals for this purpose? As a practitioner, I agree with Anne that constant/bitmask APIs are more annoying to use, and don't seem to offer any obvious gain other than the emotional appeal of cargo-culting a C best practice.
Comment 11 Brendan Eich 2011-11-22 06:12:06 UTC
(In reply to comment #10)
> Bitmasks are not a good user-facing API for JavaScript. Anecdotally, SproutCore
> 1.x used bitmasks for some state management code in our data store API, and it
> was one of the most confusing (and "weird") parts of that API.

Can you say more? What were people confused about? If it's the botched C operator precedence heritage, that's informative -- that poisons the whole well.

> Is there anything lost by simply using String literals for this purpose? As a
> practitioner, I agree with Anne that constant/bitmask APIs are more annoying to
> use, and don't seem to offer any obvious gain other than the emotional appeal
> of cargo-culting a C best practice.

Use cargo culting properly. There are no fake airplanes here, flags and bitwise operators work. The question is about usability, not sentiment or pre-scientific pattern-matching.

/be
Comment 12 Yehuda Katz 2011-11-22 06:27:43 UTC
Why are bitmasks superior to boolean properties?

(In reply to comment #11)
> (In reply to comment #10)
> > Bitmasks are not a good user-facing API for JavaScript. Anecdotally, SproutCore
> > 1.x used bitmasks for some state management code in our data store API, and it
> > was one of the most confusing (and "weird") parts of that API.
> 
> Can you say more? What were people confused about? If it's the botched C
> operator precedence heritage, that's informative -- that poisons the whole
> well.
> 
> > Is there anything lost by simply using String literals for this purpose? As a
> > practitioner, I agree with Anne that constant/bitmask APIs are more annoying to
> > use, and don't seem to offer any obvious gain other than the emotional appeal
> > of cargo-culting a C best practice.
> 
> Use cargo culting properly. There are no fake airplanes here, flags and bitwise
> operators work. The question is about usability, not sentiment or
> pre-scientific pattern-matching.
> 
> /be
Comment 13 Brendan Eich 2011-11-22 06:34:33 UTC
(In reply to comment #12)
> Why are bitmasks superior to boolean properties?

I'm not sure where that q came from. I'll give an a if you answer my q below :-|.

Boolean properties of an object are ok if you want to pass a bunch of flags and you don't care about doing set union, intersection, or difference (at least not efficiently for the case I mentioned: dense sets in [0,31]).

We're arguing in this bug not about best practices for most APIs, rather about expressiveness in WebIDL, a language used for lots of APIs.

/be

> (In reply to comment #11)
> > (In reply to comment #10)
> > > Bitmasks are not a good user-facing API for JavaScript. Anecdotally, SproutCore
> > > 1.x used bitmasks for some state management code in our data store API, and it
> > > was one of the most confusing (and "weird") parts of that API.
> > 
> > Can you say more? What were people confused about?
Comment 14 Yehuda Katz 2011-11-22 06:45:14 UTC
(In reply to comment #13)
> (In reply to comment #12)
> > Why are bitmasks superior to boolean properties?
> 
> I'm not sure where that q came from. I'll give an a if you answer my q below
> :-|.
> 
> Boolean properties of an object are ok if you want to pass a bunch of flags and
> you don't care about doing set union, intersection, or difference (at least not
> efficiently for the case I mentioned: dense sets in [0,31]).
> 
> We're arguing in this bug not about best practices for most APIs, rather about
> expressiveness in WebIDL, a language used for lots of APIs.
> 
> /be
> 
> > (In reply to comment #11)
> > > (In reply to comment #10)
> > > > Bitmasks are not a good user-facing API for JavaScript. Anecdotally, SproutCore
> > > > 1.x used bitmasks for some state management code in our data store API, and it
> > > > was one of the most confusing (and "weird") parts of that API.
> > > 
> > > Can you say more? What were people confused about?

People were confused primarily because the bitmask API operates at a level of abstraction that is markedly different from the rest of the abstraction. Here is an example of the API in action:

    if (record.get(
Comment 15 Aryeh Gregor 2011-11-23 14:28:55 UTC
(In reply to comment #11)
> Can you say more? What were people confused about? If it's the botched C
> operator precedence heritage, that's informative -- that poisons the whole
> well.

It's not operator precedence.  It's confusing even when you're just doing if (foo & bar).  It's the entire concept of bitfields.  As Yehuda says, C programmers are used to thinking of variables as actually being sequences of bits.  A string might be a pointer to an array of characters encoded as UTF-8 and terminated by a null byte, and an int might be a 32-bit value in two's-complement format.

JavaScript programmers who don't have exposure to lower-level languages are used to thinking of values as higher-level quantities independent of their format.  They'll think of strings as a sequence of characters, without thinking about encoding or delimiters or storage method.  They'll think of numbers as just numbers, not necessarily encoded in any specific format.

Of course this is wrong -- strings are actually binary-safe UCS-2 with kludges for non-BMP characters, and numbers are IEEE floating-point that occasionally behave like two's-complement integers.  And JS gurus do understand this, as do people who know lower-level languages.  But your average casual scripter just thinks of numbers as numbers.  He probably knows in theory that the number is stored as binary, but might not be proficient enough in binary to easily translate 46 to 101110.  So 46 & 32 makes no sense -- it means 32 sometimes, 0 the rest of the time, with no obvious reasoning unless the scripter actually delves into the nitty-gritty of how the number is internally represented, which he otherwise has no reason to ever do.  node1.compareDocumentPosition(node2) & Node.DOCUMENT_POSITION_PRECEDING is going to have to be copy-pasted voodoo magic to such a person.

A design goal of the web platform should be that scripters should not *have* to be exposed to things like binary except where strictly necessary.  Yes, it's obvious to us.  We also know about garbage collection and deadlocks and context-switching overhead and the proper use of hash tables vs. linked lists and the performance tradeoffs of busy loops vs. blocking.  But this is the sort of thing you shouldn't have to think about in high-level languages, because most people find it hard and confusing.

This is all in addition to the fact that this pattern of using bitfields is a lot more verbose than string literals or dictionaries, and probably has no performance benefit in the typical case.  Node.DOCUMENT_POSITION_PRECEDING involves retrieving an object property already, and it can't be easily optimized away by the JIT because window.Node is writable.

(In reply to comment #13)
> Boolean properties of an object are ok if you want to pass a bunch of flags and
> you don't care about doing set union, intersection, or difference (at least not
> efficiently for the case I mentioned: dense sets in [0,31]).

For dense sets in [0,31], bitfields are still more verbose and less usable.  You have

  if (foo & Foo.a & Foo.b & ~Foo.c & Foo.d)
vs.
  if (foo.a && foo.b && !foo.c && foo.d)

Of course the former can in principle be made more efficient, like if you use that expression many times and cache it as var bar = Foo.a & Foo.b & ~Foo.c & Foo.d.  But requiring developers to specify array lengths and types in advance is also more efficient, and we still don't.  For use-cases where efficiency is critical, we can expose special highly efficient low-level APIs, like Typed Arrays.  For almost all use-cases, we should expose APIs that are as easy to use as possible, as long as they're not really egregiously inefficient.

Not to mention, I'm not aware of any cases where we use dense sets in [0,31].  JS bitfield APIs I'm aware of typically only have a few flags, of which authors will be interested in at most one or two per call.

> We're arguing in this bug not about best practices for most APIs, rather about
> expressiveness in WebIDL, a language used for lots of APIs.

That's fair if you can come up with at least one actual example of a WebIDL interface where named constants are a good idea.  If there are a whole bunch where it's a bad idea and none where it's a good idea, that suggests it's better to strongly discourage it.  It's not like renaming const to legacyconst would bar spec authors from using it in new APIs, it would just send a strong hint that they should look into why it's called that before they use it.
Comment 16 Brendan Eich 2011-11-23 19:21:32 UTC
(In reply to comment #15)
> For dense sets in [0,31], bitfields are still more verbose and less usable. 
> You have
> 
>   if (foo & Foo.a & Foo.b & ~Foo.c & Foo.d)
> vs.
>   if (foo.a && foo.b && !foo.c && foo.d)

Read what I wrote again: union, intersection, differencing. I never write "membership testing". Yeesh.

Your overlong comment ignores actual use-cases of numeric consts in WebIDL, notably https://www.khronos.org/registry/webgl/specs/1.0/.

Do not impose policy at the level of mechanism.

/be
Comment 17 Tab Atkins Jr. 2011-11-23 21:24:26 UTC
(In reply to comment #16)
> (In reply to comment #15)
> > For dense sets in [0,31], bitfields are still more verbose and less usable. 
> > You have
> > 
> >   if (foo & Foo.a & Foo.b & ~Foo.c & Foo.d)
> > vs.
> >   if (foo.a && foo.b && !foo.c && foo.d)
> 
> Read what I wrote again: union, intersection, differencing. I never write
> "membership testing". Yeesh.
> 
> Your overlong comment ignores actual use-cases of numeric consts in WebIDL,
> notably https://www.khronos.org/registry/webgl/specs/1.0/.
> 
> Do not impose policy at the level of mechanism.

The only case of a bitfield I could find in WebGL was the argument to the clear() function, which seems to be bad in similar ways to other things argued in this thread.

WebGL makes extensive use of constants as simple arguments (not, as far as I can tell, useful to use as sets which can be unioned, intersected, or differenced), which appears to also be bad in the way already argued in this thread.  All of these appear to exist solely because of copypasta from the C API, and could be replaced by a string instead in the JS API which would be shorter and likely more readable.  In some cases, like getParameter and getBufferParameter, the API is *really* bad and should, in JS, just be individual properties or methods.  I would hope you're not arguing for that pattern to be something to design towards!  Overall, the WebGL API is *horrible* from a JS standpoint, purely so it can interoperate more directly with the C API.

There are some valid uses of constants within JS.  The Math object exposes several.  The TypedArray objects expose their width as constants as well.  These are much rarer than the bad uses.
Comment 18 Aryeh Gregor 2011-11-25 20:21:47 UTC
(In reply to comment #16)
> Read what I wrote again: union, intersection, differencing. I never write
> "membership testing". Yeesh.

This might theoretically be an advantage, but I haven't yet seen an actual use-case where it's important.  If this were common, it could be fixed by introducing union/intersection/difference functions or operators for arrays and/or objects.  Given that it's not common, authors can just write these functions themselves and it's no real loss.

> Your overlong comment ignores actual use-cases of numeric consts in WebIDL,
> notably https://www.khronos.org/registry/webgl/specs/1.0/.

It looks to me like those constants are present only for compatibility with preexisting APIs, in this case C APIs.  That use would be completely inappropriate for a JS API that's being made from scratch, so the term "legacyconst" is suitable.

> Do not impose policy at the level of mechanism.

I don't suggest we ban consts, just make it clear that they're not recommended for new APIs.  We already do it for legacycaller.

Web standards are developed in a decentralized fashion, a lot of them by people who work primarily in C++ and aren't intimately familiar with either JavaScript programming or the web standards world.  Once they're implemented, sometimes by the same small group that wrote the specs without much outside review, any mistakes get set in stone.  Those of us who are working on core standards like WebIDL or DOM4 should be working to convey best practices to everyone working on more peripheral standards that get less review.  Naming in WebIDL is one of the very few places where we can actually get a message out to a lot of spec writers with very little effort.
Comment 19 Cameron McCormack 2011-12-06 05:50:16 UTC
Constants are not nearly as evil as callers.  Let's leave ugly names ("legacyblah") to truly egregious features that we need only for describing legacy features.

Aryeh, please indicate for the Disposition of Comments document whether this resolution is acceptable, thanks.
Comment 20 Anne 2011-12-06 10:33:55 UTC
There should at least be a big fat warning there.

<p class=warning>Discuss platform usage of 'const' with public-script-coord@w3.org before proceeding.</p>
Comment 21 Cameron McCormack 2011-12-07 01:08:19 UTC
(In reply to comment #20)
> <p class=warning>Discuss platform usage of 'const' with
> public-script-coord@w3.org before proceeding.</p>

OK, I added a (slightly wordier) warning.
Comment 22 Aryeh Gregor 2011-12-27 14:22:14 UTC
The resolution is okay with me.