Value types with suffixes
One of the most intriguing parts of the earlier value object proposals was the ability to register numeric suffixes for value types, so you could write things like 2500u and have it be equivalent to uint64(2500).
This wound up being a critical piece of my plans for a future better CSSOM API, so authors could write things like el.css.width = 50px;, where CSSpx() is a value type that registers for the px suffix. This is really nice, since it lets you write JS that looks almost exactly like CSS.
Any plans to still do this?
When we talked about this at TC39, there definitely were plans. In fact, it's one of the easier things to do independently, so conceivably it could be finished ahead of the rest.
The idea of 2500u -> u(2500) gets you 80% of the way there, but then you realize 999007199254740991u doesn't work (it becomes u(999007199254741000)). We left things at u("999007199254740991") which seems fine. Almost too easy...
Tagging in @BrendanEich in case he's not watching this repo, since he was involved in that conversation.
Idea is indeed to transpose and map the generally too-short/conflict-prone suffix name to a maker function name, e.g., L to int64, UL to uint64, etc. And to quote digit strings that require more precision than IEEE double.
Working on this for next month's meeting. Operators are the bigger deal. We made some progress at the last meeting. More elsewhere, these proposals are independent of typed objects.
/be
Idea is indeed to transpose and map the generally too-short/conflict-prone suffix name to a maker function name, e.g., L to int64, UL to uint64, etc.
I can see the attraction of this, but am worried that registries are so annoying to get right, and have an unclear scope, versus simple lexically-imported import { literalSuffix as n } from 'my-big-int'. In this scenario we'd pre-populate global.L, global.UL so that they are generally in scope unless you override them.
It's a bit more fragile in some ways (your code stops working because you did var n = 0), but less in others (two of your dependencies added n to the global registry with different meanings).
Hmm, I suppose we could tie it to module import syntax, so that identifiers that are not imported from a module do not get the 2500u -> u(2500) treatment. We'd still have a bit of hand-waving that every ES program contains a import { int64 as as L, uint64 as UL } from 'js/typed-objects' prelude, but seems nice otherwise.
You do not want lexical-only suffix resolution, it will make trouble with code that uses i, u, L, UL, etc.
Modules point the way. Module names are not lexical identifiers. They are mapped via a per-realm table. Same applies to thinking on literals and operators -- provided certain monotonic properties are upheld.
/be
It's a bit more fragile in some ways (your code stops working because you did var n = 0), but less in others (two of your dependencies added n to the global registry with different meanings).
These seem to exactly balance each other. If you're worried about two libraries assigning different functions to the same suffix, it seems you'd be equally worried about one library defining a suffix function and another defining a different function of the same name.
And losing the ability to use i or n as variables is a pretty huge cost, imo.
I expect that libraries which provide new suffixes (like the CSSOM) will have a function that triggers their addition to the table, so you can de-conflict yourself if necessary.
You could also go the Lisp-2 route, and just let the same name mean different things in different contexts. suffix L = int64; would introduce a lexical L in the suffix namespace assigned to the int64 function, only retrievable by using as a suffix or via something like getSuffixValue("L"). Using L as a variable name wouldn't conflict with this at all. (Common Lisp puts values and functions in different namespaces, so you can use length as a variable despite a (length) function existing. It actually has a bunch more lesser-used namespaces, too. Since CL was my formative language, I found it really weird when I started using C++ and JS and realized that functions could collide with variables.)
@tabatkins, good point about the Lisp-2 route, I considered that. Special form is special ;-). I think transposing and mapping to the maker function name wins on balance. Just transposing loses.
/be
Agree that transposing and mapping wins over just transposing. Also agree that multiple namespaces is extra complexity, and possibly more than is warranted here. Hopefully we'll see some use-cases presented as this advances, so we can tell whether lexical suffixes are necessary; if not, definitely stick with the simple global registry.
Anyway, I'm just happy to hear that this is still on the table; it was missing from the docs so far, so I was worried.
On Thu, Aug 21, 2014 at 01:24:04PM -0700, Brendan Eich wrote:
You do not want lexical-only suffix resolution, it will make trouble with code that uses i, u, L, UL, etc.
I am very skeptical of global tables for suffixes. I'd rather have
no suffixes at all. A global table for suffixes is just asking for
trouble: if I grab two libraries that both want the suffix pt, I
have no recourse. The basic problem is that the "key" to the global
table is just an ASCII string, with no form of namespacing.
In contrast, a global registry for overloaded operators can work just
fine. This is because that registry would be keyed by (presumably) the
prototypes of the values involved. So if I use two distinct libraries
that each define a type Point with their own operators, there is no
problem, because each variant on Point will have its own prototype.
My personal preference at this point is to have some kind of lexically-based import. I see various options:
- Simple transpose, combined with a convention that suffixes use
uppercase letters. Hence
15Ior15L. This means that it won't conflict with short variable names likei. Or perhaps a convention of a leading underscore15_l. Or perhaps you write15ibut it looks up the functionsuffixI. Point is, some convention.- Pro: Simple.
- Con: Uppercase doesn't look as good? Maybe?
- Declare suffixes using a distinct syntax (as proposed by others
on this thread).
suffix i = function(...).- Pro: Explicit and clear.
- Con: Kind of wordy. You'd like a nice way to "import" suffixes succintly from a library.
I'm sure there are more, however.
Either of these seem preferable to a global registry, which is guaranteed to cause conflicts between people attempting to use multiple libraries.
Agree global registries without some way to compose usually stink.
The module system's loader has a global registry, OTOH -- people want to rendezvous on module names, that's part of the deal. For common value types, the usual suffixes will be uncontested as suffixes, but still likely to collide with short local variable names. Could we use the module registry? Just a half-baked thought, I wanted to get it out here.
Niko Matsakis wrote:
- Simple transpose, combined with a convention that suffixes use uppercase letters. Hence
15Ior15L. This means that it won't conflict with short variable names likei. Or perhaps a convention of a leading underscore15_l. Or perhaps you write15ibut it looks up the functionsuffixI. Point is, some convention.
- Pro: Simple.
- Con: Uppercase doesn't look as good? Maybe?
The _i convention is anti-usable. JS has to compete, both batteries included (f for float32, L and UL for the int64s, n for bignum) and with user-defined types. If you don't mandate leading _, just make it an advisory convention, then it will very likely be weeded out in the Darwinian ecosystem.
- Declare suffixes using a distinct syntax (as proposed by others on this thread).
suffix i = function(...).
- Pro: Explicit and clear.
- Con: Kind of wordy. You'd like a nice way to "import" suffixes succintly from a library.
This has been on the table in the past, going back to ur-JS2:
http://web.archive.org/web/20110216074628/http://www.mozilla.org/js/language/js20-2002-04/core/expressions.html#unit-expression
It does win on both usability and compositionality. If we could avoid it and just use lexical identifiers, and not have collisions due to f and i and the like, I'd be happy.
Bigger-picture: sweet.js can do lots of stuff including suffixes and units, so if we eventually standardize sweet-style macros on top of static modules, we may well regret prefiguring macros for units/suffixes with a dedicated suffix syntax (even if it were future-proofed so as to be equivalent to a macro).
To avoid dragging this out any further, let's start with just transposing and using lexical bindings. Per email with Niko I'd like to do this in a separate repo, since operators and suffixes go together but are not tied to value objects/types or typed objects in particular. Everyone ok with separating repos/concerns?
/be
Everyone ok with separating repos/concerns?
Yep. In fact I'd say operators and suffixes seem pretty separable themselves.
While we're still in this repo, one weird idea I had is to say that module import bindings are special and 1080i will not exactly look for i lexically, but instead will look for an imported binding named i. So if you declare a local variable named i it ignores that, instead searching up to the top-of-module-scope for an imported binding i. This is pretty weird though as it makes module import bindings special and feels like it's hijacking the module system in strange ways. Just thought I'd throw it out there.
Throwing things out is fine, says I :-P.
@nikomatsakis, any reactions to these module-musings?
/be
On Thu, Aug 28, 2014 at 09:24:58AM -0700, Brendan Eich wrote:
The _i convention is anti-usable. JS has to compete, both batteries included (f for float32, L and UL for the int64s, n for bignum) and with user-defined types. If you don't mandate leading _, just make it an advisory convention, then it will very likely be weeded out in the Darwinian ecosystem.
I agree _i is not great. Mostly I wanted to say that the name you
lookup doesn't to be 1-for-1 identical to what you write as the
suffix.
Bigger-picture: sweet.js can do lots of stuff including suffixes and units, so if we eventually standardize sweet-style macros on top of static modules, we may well regret prefiguring macros for units/suffixes with a dedicated suffix syntax (even if it were future-proofed so as to be equivalent to a macro).
Interesting thought.
To avoid dragging this out any further, let's start with just transposing and using lexical bindings. Per email with Niko I'd like to do this in a separate repo, since operators and suffixes go together but are not tied to value objects/types or typed objects in particular. Everyone ok with separating repos/concerns?
+1