-
Notifications
You must be signed in to change notification settings - Fork 62
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Collect developer feedback about the ergonomics of #{ }
/#[ ]
(vs alternatives @[ ]
/@{ }
or {| }
/[| ]
)
#10
Comments
For now it does not seem to bug people I showed that to but we should try to get more feedback surely |
How would |
That's a good question, I should address that in the proposal text |
I don't think it's a good idea to overload the |
I agree about not overloading |
@const
const
Ok so here is what we've seen so far:
|
I honestly do have a personal preference with |
I agree with this. This is one of the first issues I found with the proposal. It shouldn't be hard to pick a new keyword that doesn't imply equality with the existing semantics of I think I would even personally prefer |
It may not be necessary for compilers to parse the code properly, but I think it would be helpful to make things clear to developers. To draw a parallel: the |
A new keyword would help in conversation (casual or educational):
Versus "I've got this immutable value". |
That makes sense, I'll talk with my co-authors and see what we should do! |
I am deeply, deeply troubled by the conflation of Speaking as a frequent teacher of these exact topics ( The claims in the README that |
This is completely relevant and I think I can definitely put aside my affinity to what C++ would do (as a matter of fact const const in C++ is a pain to teach) so let's find a replacement! |
Just to be clear, I love the feature. I want JS to have immutable data structures. I just don't like calling them "Constant Objects" or using FWIW, "immutable" may or may not be the right word, either. Generally, immutable data structures (in userland) mean data structures that allow structured mutations (via sort of copy-on-mutate behavior). If these are intended to have something like that -- awesome! -- then "immutable" is a good word. But if not, if they're more like a deeply frozen object, then I think "frozen" or "read-only" are the more appropriate term than "constant" or "immutable". EDIT: After some clarifications in various tweet threads, I actually think "fixed" is better than "frozen" / etc. EDIT 2: if we go in the direction of "immutable", I like |
Since |
An alternative syntax (and naming) could just be to use two decorators or tagged object literals and use the fairly popular "tuple" and "record" names: e.g. With decorators: const vector = @Tuple [3, 4]
const point = @Record { x: 10, y: 20 } e.g. With tagged literals: const vector = Tuple#[1, 2]
const point = Record#{ x: 0, y: 10 } |
I agree that I can see |
What about a |
One possible issue with the keyword approach is using this feature with primitives, specifically a template literal, since that would be ambiguous with template tags, or with regexes: var x = fixed "string"; // ok
var y = fixed `string`; // oops
var z = fixed / a / gi; // oops There's no advantage of using these with primitives, per se, but I also think it might be strange (with a keyword rather than syntax) to explain that it can only be used with array/object values and not other primitives. The mental model I'm using here is that a value-type is basically a bit like defining some arbitrary array/object as a single primitive value unit. So it'd be a shame if this feature couldn't reflect that symmetry. |
Would that only be an issue for a keyword (e.g. `fixed) if it’s already in use in a lot of code (e.g. in a tagged template literal)?
I agree. It seems like there are two fundamental questions worth investigating: What to call it (e.g. const, fixed, final, immutable, etc.) and what kind of language structure should be used (e.g. keyword, at-prefixed name, the weird bracket syntax). |
A note on |
@getify wait, what's wrong with this? var y = fixed `string`; // oops |
@arxpoetica that already works by using the identifier “fixed” as a template tag. |
oh oh yeah. hmm. so maybe var y = @fixed `string` ? |
Among the following syntaxes : // const
const obj = const { a: 1, b: 2 };
// fixed
const obj = fixed { a: 1, b: 2 };
fixed obj = { a: 1, b: 2 };
// final
const obj = final { a: 1, b: 2 };
final obj = { a: 1, b: 2 };
// frozen
const obj = frozen { a: 1, b: 2 };
frozen obj = { a: 1, b: 2 };
// immutable
const obj = immutable { a: 1, b: 2 };
immutable obj = { a: 1, b: 2 };
Also, I prefer the RHS solution because it means that you can do : let obj = immutable { a: 1, b: 2 };
obj = null; // OK
obj = { ... }; // OK
obj = immutable { ... }; // OK
function fn() {
// ...
return immutable { ... }; // OK
}
obj = fn(); // OK and reuse the immutable obj = { a: 1, b: 2 };
obj = null; // ???
obj = { ... }; // ???
obj = immutable { ... }; // Not possible just like you can't do `obj = const { ... };`
function fn() {
// ...
return immutable { ... }; // Not possible just like you can't do `return const { ... };`
} Now if a 9-letter keyword is too long I think |
Please forgive me if I am not fully understanding this proposal but what's the advantage of explicitly assigning the structure to a variable? If a keyword is used could you not just go the way of fixed map1 {
a: 1,
b: 2,
c: 3,
};
const map2 = map1 with .b = 5;
assert(map1 !== map2);
assert(map2 === fixed { a: 1, b: 5, c: 3}); //<- something like an anonyomous fixed If this were the case I would think something like |
I also don't know what the possibility if it being "called" like a function as well instead of using const map2 = map1{ b: 5 };
//or with an array
const array2 = array1{ 0: 'x' } As far as ergonomics go that's pretty easy. I know this doesn't say much about the understand-ability, teach-ability, or possible confusion when reading at a quick glance. |
Alright everyone, we do appreciate the feedback but let's keep that issue on point: the keyword. I don't think the From there if we do not go with const we'll have two other solutions:
I'm going to rephrase the proposal so it is open ended between |
From a certain perspective there is already a long-form available if an author feels that would help with readability: Though these are technically slightly less efficient with the extra allocation and function calls, though that may only be an issue in performance sensitive code paths. |
You can create objects with But I think it's pretty strong consensus that I'm not claiming such because it's shorter, but because it's so much more recognizable due to its declarative nature. I think JS would be worse-off if the reverse were true, and everyone preferred and used the constructor-form over the literal form. I would expect, and hope, that whatever syntax we pick for records/tuples will, eventually, achieve the same "universal best practice" preference over whatever long-form API constructor form (if any) has also been added. That should be our goal, anyway (IMO). |
What makes Record/Tuple literal more necessary than Map/Set? |
I do kinda wish Map/Set had a literal syntax form. If such a form existed, I'd prefer it over the constructor forms. |
They could if we could add |
Just like many people in this thread (@silverwind @arxpoetica @zenflow ...) I completely agree with this. I mean, having immutable structures baked in, is one of the best things that could happen to JavaScript. But I think it would be much clearer to have a new keyword instead of the So like many in this thread I would propose a new keyword, in this case Reasons:
|
The reason |
@svieira Your example is interesting because you could almost make that a language feature where: Therefore, presuming you would you would create a new record with const R = Record
const myRecord = R#{ plainObject } Then people could choose the ergonomics they wanted, to some degree. But of course that's not quite as sweet / simple as To do the Map example, they could do: let map = Map#[
["1", "first element"],
[1, "second element"],
[true, "hello world"]
]; Buuuuuut I wonder if all of that becomes hella complicated when you want to represent / serialize the thing you've just created. Or if that's just too simple of syntactic sugar to be useful. That said, I just logged the above Map in Chrome and it logs as:
Which then leads to this rational (and very good) question from @dy:
Like... if there's no "easy" syntactic way to define Map or Set, why are Records and Tuples special in that regard? They're all collections. I guess the question has been sort of reversed as, "We have simple literals for arrays and objects, so why wouldn't records and tuples?" But you could almost answer that with: "Because JavaScript history, but as we added more types of collections, we didn't add new literals each time." To me, it almost reads better to have something serialized like:
And not:
So it kinda begs the question of the value of defining it using a special syntax, especially, yeah, with the existence of Map and Set. It really depends on from which direction you're approaching an idea. Is this an "extension" of arrays and objects? Or are these new collections, just like |
I think the biggest reason we want syntax for records/tuples is the presumption that it will be quite common to pre-define values with N levels deep of nesting. A "keyword" approach gets much uglier when you have to repeat it at every level of nesting, even if that keyword is a single character. As to why records/tuples but not maps/sets literal syntax, I'm just sorta estimating here based on anecdotal observation, but it seems much more rare to me that you pre-define maps/sets (they're more often used for programmatically filled in data) and even less often have I seen maps/sets heavily nested. It's pretty common to see objects/arrays as literals with plenty of nesting. Perhaps we'd see more pre-defined maps/sets (with nesting) if there was a literal syntax for it... not sure, I think it's hard to ascertain cause/effect/correlation. Another thing to point out about map/set constructor forms... both constructors have a pretty useful capability to receive, as input, arrays of values or key/value tuples to construct the map/set from. We don't really have such capability without the constructor, so a literal form seems somewhat less powerful or useful. |
IANA VM Implementor, but I would assume that there are also performance
considerations here. With special syntax, the VM can skip instantiating a
whole extra object/array and allocate the record/tuple directly, whereas if
it's just syntactic sugar over a general constructor call, that's much less
feasible (to say nothing of the extra validation, nested structures, etc).
One of the goals of R&T (IIUC) is that it be pretty performant, and the
dedicated syntax is one part of that.
As a maintainer of a very large codebase, I can speak to some of the
questions about special Map/Set syntax. We generally recommend using Map
(or Set) over "dictionary-style" objects (i.e. objects accessed with
brackets), but for top-level constants, this advice is even less widely
taken. It's just too convenient to be able to use the object literal
syntax. (Immutability is a whole other issue - even if we could define a
Map with a literal, I'd still wish it were truly immutable in the above
cases. Records and tuples are at least immutable, so I'd expect to start
recommending nearly every top-level object/array constant be replaced with
R&T literals.)
|
This is an excellent point! I remember this occurring to me a long time ago, that these JS collections would probably map to optimized C structs (or something like that), but then I forgot about it. Thank you!! @getify Also excellent points. It was just kind of a derp moment, like, "Yeah... WAIT a minute..." |
Adding my 2 cents as mentioned before, and also on #82 and #9 I'm really leaning towards @getify in calling it using the imm name = { obj: 1 }
immutable other = { other: 'obj' }
const literal = #{ yet: 'another' } Is something that makes it a lot clearer for me. |
@khaosdoctor I think it's important for the indicator of record/tuple to be attached to the value expression rather than to the declaration. There are lots of places values exist in programs that aren't part of being assigned first to a lexical identifier. For example, passing a value as an argument to a function call, or assigning the value to a property in an object. |
放开私有属性和方法关键字符号化后,JavaScript 在脚本语言的路上越走越远了。但是 JavaScript 的应用场景越来越需要面向对象了,而不是胶水语言。我们在设计语言时,应关注本身形态和灵魂,而不是符号化去逃避历史负担。 After letting go of private property and method keyword tokenization, JavaScript went further and further as a scripting language. But JavaScript application scenarios increasingly require object-oriented, rather than glue language. we should focus on its own form and soul, rather than symbolize it to escape the burden of history. |
@xianghongai im not sure what you’re getting at exactly. Nothing about JS is “glue language”, including private fields, records, or tuples, and most JavaScript applications i see these days actively avoid object-oriented code in favor of functional code. |
@getify 's
Makes sense, so you mean that: const name = imm { obj: 1 }
const other = immutable { other: 'obj' }
const literal = #{ yet: 'another' }
function foo (bar) {}
foo (immutable { a: 1 })
foo (imm { a: 1 })
foo (#{ a: 1 }) Would be better? Makes a lot of sense to me, it'd be prettier with highlighting but I'm not against it. |
@khaosdoctor Your examples only sort of work when the immutable value is the "outer" value. How would it look when it is deeply nested within another object or array? const immutable = 'immutable';
const myArray = ['immutable', immutable, { other: 'obj' }, immutable { other: 'obj' }, immutable { other: immutable }, { other: immutable {} }] I feel like it starts to feel messy really quickly. |
FMI, in which comment was the |
After using consistently the The main pushback against it seems to be based personal preferences, which also happens when trying out other alternatives. Unlike what we discussed earlier here, we haven't managed to do neutral polling on this, but regardless, the change would be harder to enact after the time we spent discussing the current version if such polling were to be made. We still collected data points by interviewing people and syntax raises two kind of responses:
Going forward, we are going to keep |
class Example {
#field = #{ id: 1234 }; 😕
} Hash symbols are haunting me. JavaScript is starting to look like Perl. Perhaps Anders Hejlsberg could assist with the language design? (I don't mean that in an insulting way, I just don't think that things are moving in the right direction.) |
Aesthetics are incredibly subjective, as is PTSD from specific other programming languages. |
As for me, this is a good choice. We can't pick a unique char for each case as someone wants, but |
Yeah wrote a plug-in that changes my preferred syntax to this before compile time. Solved my problem with the proposed syntax :) |
great, that would be WeyertScript! ;-) |
As mentioned in #10 (comment) |
An issue with |
Yay! Can't wait to see this implemented in engines and be able to use it for all kinds of applications. |
Notice by @rricard: This issue is now only open to discuss the following alternatives:
#{ }
/#[ ]
,@{ }
/@[ ]
,or{| |}
/[| |]
{| }
/[| ]
Original issue text:
Most people I've talked to are pretty positive about
@const
in two ways:#{ }
/#[ ]
) might be too cryptic and confusing.@const
makes sense, since it's just like declaring a variable const, but it goes deeply through the data structure.It'd be good to continue collecting feedback to understand if these intuitions are widely shared.
The text was updated successfully, but these errors were encountered: