Skip to content
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

Update JS to Dart #4354

Merged
merged 2 commits into from Nov 21, 2022
Merged

Update JS to Dart #4354

merged 2 commits into from Nov 21, 2022

Conversation

atsansone
Copy link
Contributor

@atsansone atsansone commented Nov 8, 2022

Staged page.

Original comments are in a Google Doc.

@parlough parlough self-requested a review November 8, 2022 20:06
@atsansone atsansone requested a review from lrhn November 8, 2022 20:08
@atsansone atsansone added the review.tech Awaiting Technical Review label Nov 8, 2022
Copy link
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments. Most stylistic.
The initial section on "primitive types" really doesn't work for me. The original wasn't better.
I'll try doing a rewrite myself.

checking for that identifier.
Both JavaScript and Dart use data types.
Types determine what kinds of values can be stored and what operations can be performed on a variable of that type.
Dart differs from JavaScript in that it identifies as a strongly typed language.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The below sounds more like it describes a statically typed language,
which is also what we otherwise say Dart is: https://dart.dev/faq#q-is-dart-a-statically-typed-language.

(I know "strongly typed" is being used inconsistently on the interwebs, and it's probably also used like this.)

Pedantically, both Dart and JavaScript are strongly typed, in the sense that a value has a specific inherent type, which doesn't change over time. An example of a non-strongly typed language is C/C++, where you can reinterpret_cast a value (really a chunk of bytes in memory) to a completely different type.

Dart is statically typed in that we assign a static type to every expression and variable.
(And it's soundly typed because the runtime type of the value of an expression is correctly predicted by the static type. Sound static typing requires static typing, JavaScript does not have static typing.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some changes.

or assigned as `dynamic`, which disables static type
checking for that identifier.
Both JavaScript and Dart use data types.
Types determine what kinds of values can be stored and what operations can be performed on a variable of that type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typed variables is something that JavaScript doesn't have, so it feels weird after a "Both languages" sentence.

inferrable by the analyzer, explicitly defined,
or assigned as `dynamic`, which disables static type
checking for that identifier.
Both JavaScript and Dart use data types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what "data types" mean here, or what "using" them means.

Both JavaScript and Dart values have consistent runtime types. A value doesn't change what type it has over time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with a better definition.


Dart supports nullable and non-nullable versions
of the the following built-in types:
Dart supports nullable and non-nullable versions of the following built-in types:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dart supports nullable and non-nullable versions of all types (depending on how you think about the type Null itself). I wouldn't mention nullable types, or nullability, first here, just list the built-in types.

(Not sure I'd include Symbol in the list. There are lots of platform types, but some are more fundamental than others. I don't think Symbol should make that list, not as much as the other ones here.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

const bat =
Object.assign(
new Bat(),
new Flyer()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works, but it relies on the fly/walk "methods" being declared as fields instead.
If you wrote it as

class Flyer {
  fly() => console.log('Flaps wings');
}

instead, which would be the canonical way to write a JS class, the assign would not copy the method (because it's on the prototype and is not enumerable).
Might be a caveat worth mentioning.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this describes a specific caveat for JS, I'll leave it as is.

This means that you can access private members from code in the same library.
By default, this works anywhere in the same file.
Private class members can be accessed from code in the same file.
Using the `part` directive to expand the scope of a library scope a single file.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Word missing? Maybe "beyond a single files"?

By default, this works anywhere in the same file.
Private class members can be accessed from code in the same file.
Using the `part` directive to expand the scope of a library scope a single file.
When possible, [avoid doing so using `part`][]. The common practice would be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just [avoid using `part`]. There are other reasons to use part files and you should avoid those too if you can.

when the field is first accessed,
rather than on initialization.
In the preceding example, the compiler does not know to assign
`captures` if `capture` is true. In this case `capture` is true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop "In this case capture is true,", we don't know that. The logic is sound whether it's true or not.

Copy link
Contributor Author

@atsansone atsansone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lrhn : Made the requested changes.

inferrable by the analyzer, explicitly defined,
or assigned as `dynamic`, which disables static type
checking for that identifier.
Both JavaScript and Dart use data types.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with a better definition.

checking for that identifier.
Both JavaScript and Dart use data types.
Types determine what kinds of values can be stored and what operations can be performed on a variable of that type.
Dart differs from JavaScript in that it identifies as a strongly typed language.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made some changes.

Dart differs from JavaScript in that it identifies as a strongly typed language.
In practice, this means that all Dart types must meet one of three conditions:

1. The analyzer can infer the type from the operations run on the variable.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


Dart supports nullable and non-nullable versions
of the the following built-in types:
Dart supports nullable and non-nullable versions of the following built-in types:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Dart doesn’t have the concept of abstract equality,
so the `==` and `!=` JavaScript operators have no equivalent.
Unlike JavaScript, Dart doesn’t have the concept of abstract equality.
The `==` and `!=` JavaScript operators have no equivalent.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deleted the line.

to a boolean, not one of the two values like these
operators do in JavaScript.
JavaScript allows empty strings, `0`, and other values to have
inherent boolean values. JavaScript considers these values to be
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

of an object but you can override it to resemble
a data class. For more information,
of an object. You can override the hash value to resemble
a data class. To learn more,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

and always return a `Stream` instead of an `Iterable`.
Async generator functions can create streams.
These generators resemble a synchronous generator function.
These functions use the `async*` keyword and return a `Stream`.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made a slight change. I am trying to keep sentences concise and easier to follow.

const bat =
Object.assign(
new Bat(),
new Flyer()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this describes a specific caveat for JS, I'll leave it as is.

@parlough parlough requested a review from lrhn November 11, 2022 17:08
add the trailing comma. Avoid adding the trailing comma for the formatting
benefit alone.

JavaScript supports trailing commas in list and map literals only.ß
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spurious "ß"

Copy link
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, had this late yesterday, but forgot to submit.

Dart assigns a static type to every expression and variable.
In Dart, the static type predicts the runtime type of
the value of an expression.
This means Dart values have sound static typing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop that line. "Values" is a runtime concept, and "static typing" happens at compile-time.
Or maybe rather:

This means that Dart programs have sound static typing.

Every variable has an associated type.
The type determines the kind of value the variable can store and
what operations can be performed on these values.
Dart differs from JavaScript as its types never change; they remain _static_.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather, Dart has static types at all. JavaScript doesn't. It's not that JS variables have static types that change, they concept of "static type" is entirely absent in JS, and the only thing that matters is the dynamic type of values.

So, maybe:

Dart differs from JavaScript in that it assigns a static type to every variable and to every expression.
In Dart, the static type predicts the runtime type of the values of a variable, or of the value of an expression.

are implemented to be immutable (unchanging)
and are canonicalized,
which means they behave as if they are value types.
All types in Dart are of the type Object. All values are also objects.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Nit: The null object is the one value which is not an Object. More correctly, all non-Null types are subtypes of Object. Might be a mostly academic distinction at this point, though.)


Dart has three data types for holding numbers:

`num`
: The equivalent to the generic number type in JavaScript.

`int`
: Any numeric value without a decimal point.
: Any numeric value without a fractional part.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that "Any" can be confusing. There are non-fractional numeric values that are not int (1e+302 is a double with no fractional point, but is not an int. Except on the web, which muddies the message.)
Could we change "Any" to just "A".


`double`
: Any numeric value, including those with a decimal point.
: Any numeric value as a 64-bit (double-precision) floating point.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Any" here also confuses me. What is the domain it's ranging over? If they can all be doubles, is this just

Any 64-bit (double-precision) floating point number.

?
I think that might be more precise.

it doesn’t have an equivalent.
{{site.alert.note}}
When you create an Object, the class constructor must initialize the `final` instance variables.
This ensures that these variables have a value before anyone can read it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it -> them

(Refers to "these variables".)


### Nullable vs non-nullable types

For example, all the variables in the following code
are non-nullable:
All the variables in the following code example cannot be `null`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "All ... cannot" reads wrong to me.
Could it be "None of ... can"?

These functions return an iterable collection of items
computed to avoid unncessary work.

To write a generator function,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Maybe add "in Dart" here, since the previous paragraph talked about "both languages", so the context isn't completely clear.)

Adding new operators is not supported in either language,
but Dart allows you to overload existing operators
Neither language supports adding new operators.
Dart supports overloading some existing operators
with the `operator` keyword. For example:

{:.include-lang}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd just change the code example to:

class Vector {		
  final double x;
  final double y;
  final double z;
  Vector(this.x, this.y, this.z);
  Vector operator +(Vector other) => Vector(
    x + other.x, 
    y + other.y,
    z + other.z,
  );
  // maybe even add:
  Vector operator *(double scalar) => Vector(
    x * scalar,
    y * scalar,
    z * scalar,
  );
}

Adding vectors is a well-defined mathematical operation, so this example should be uncontroversial. "Adding" boxes doesn't really make sense if you try to visualize boxes.

| Strict equal | `===` | `indentical(,)` |
| Abstract equal | `==` | `==` |
| Strict not equal | `!==` | `!indentical(,)` |
| Abstract not equal | `!=` | `!=` |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be better to not equate any of them.
Or just recommend using Dart == for everything.

@atsansone atsansone force-pushed the js-to-flutter-2 branch 3 times, most recently from cfc4c0d to a02c028 Compare November 17, 2022 18:30
@parlough
Copy link
Member

parlough commented Nov 18, 2022

I'll take a final look at this tomorrow. Sorry about the delay, I wanted to wait for Lasse to finish his review.

@atsansone atsansone assigned atsansone and unassigned lrhn Nov 18, 2022
@atsansone atsansone added st.RFM Ready to merge or land and removed review.tech Awaiting Technical Review labels Nov 18, 2022
@atsansone atsansone removed the request for review from parlough November 18, 2022 16:28
Copy link
Member

@parlough parlough left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, suggestions, and issues. Feel free to push back on any. I noticed @lrhn has a few open comments that still seem relevant as well. I commented or reacted on them.

If possible, avoid squashing commits and rebasing as it made it hard to see what changed between/after reviews and sometimes messes with GitHub's review comment handling.

**Pro tip:** Dart provides [`dart fix`][],
which finds and fixes errors found by the analyzer.
{{site.alert.end}}
Dart provides [`dart fix`][] to find and fix errors that the analyzer finds.
Copy link
Member

@parlough parlough Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel someone reading this will not be familiar with what "the analyzer" is. Could we add a link or small explanation here, especially since the rest of the article refers to the "the analyzer" a few times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. I'm removing it here because, well, if dart fix finds and fixes, why does the analyzer matter here?

Comment on lines 366 to 374
// Declare a variable without a type
// and Dart infers the 'dynamic' type
var name;
// Declare a variable with a specific type
// when you don't provide an initial value
String name;
// Initialize the variable
// and the type remains `dynamic`
name = 'bob';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using the same variable name here is confusing. The comment for name = 'bob' says the type is dynamic, but the declaration before creates a name variable with a String type.

Comment on lines 382 to 383
because you initialized the variable. By convention, use `var` when the
analyzer can infer the type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention final here as well?

Suggested change
because you initialized the variable. By convention, use `var` when the
analyzer can infer the type.
because you initialized the variable. By convention,
use `var` or `final` when the analyzer can infer the type.

Comment on lines 387 to 364
var name = 'bob';
// Dart understands the variable's type to be 'String'.
// Dart can infer that type from the variable's
// initial value.
```
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit duplicated from the last entry in the previous codeblock(L375-378) and again below (L406-408).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote all of this. Woof.

Dart uses `final`. When Dart adds `final` or JavaScript adds `const`,
you must initialize the variable before _reading_ it.
When Dart adds `const`, you must initialize the variable
before _compiling_ it.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the squashed history, I'm not sure what has changed, but I think some of the suggestions here are still valid. "you must initialize the variable before compiling it" doesn't make much sense to me as a reader.

@@ -1695,15 +1840,28 @@ to their JavaScript counterparts.
### Futures

`Future` is Dart's version of a `Promise`:
an asynchronous operation that resolves at a later point.
an asynchronous operation that resolves at a later point.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This clarification makes sense and I think should still be addressed.

.catchError((err) {
print(
'Future encountered an error
before resolving.'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atsansone These should still be fixed.

and multiple methods for controlling the stream,
such as emitting new items using the `add` method,
or completing the stream using the `close` method.
The utility class [StreamController][] can create and control streams.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep this in code syntax to be consistent with elsewhere:

Suggested change
The utility class [StreamController][] can create and control streams.
The utility class [`StreamController`][] can create and control streams.

Comment on lines 2713 to +2660
Like JavaScript, Dart has no access modifier keywords:
all class members are public by default.
all class members are public by default.

While private class members are not yet officially
part of JavaScript because they are not part of any
published EcmaScript standard,
a proposal for this has been completed and is ready
to be included in the next publication of the standard.
JavaScript will include private class members in the next
practical revision of the EcmaScript standard.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole talk about the EcmaScript spec for private members not being complete is confusing and I'm not sure it should be included. Either way, it looks like PrivateIdentifiers were part of EcmaScript 2022 which has been released.

Reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Private_class_fields

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the reference specifically; privates are in stage 4, which means they will be in the next practical revision. That's why I phrased it as I did.

To indicate that Dart initializes class fields at a later point,
assign the `late` keyword to those class fields.
Those class fields remain non-nullable.
Do this when a variable cannot be observed before being initialized.
Copy link
Member

@parlough parlough Nov 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new sentence is confusing as every variable technically "cannot be (at least successfully) observed before being initialized". I think the original text was trying to say something like "Do this when a variable does not need to be(or is not) observed/accessed right away and can be initialized later".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. Fixed.

@parlough parlough removed the st.RFM Ready to merge or land label Nov 18, 2022
Copy link
Contributor Author

@atsansone atsansone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@parlough : Back to you!

**Pro tip:** Dart provides [`dart fix`][],
which finds and fixes errors found by the analyzer.
{{site.alert.end}}
Dart provides [`dart fix`][] to find and fix errors that the analyzer finds.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. I'm removing it here because, well, if dart fix finds and fixes, why does the analyzer matter here?

Comment on lines 387 to 364
var name = 'bob';
// Dart understands the variable's type to be 'String'.
// Dart can infer that type from the variable's
// initial value.
```
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I rewrote all of this. Woof.

Dart uses `final`. When Dart adds `final` or JavaScript adds `const`,
you must initialize the variable before _reading_ it.
When Dart adds `const`, you must initialize the variable
before _compiling_ it.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewrote. Should read better now.

that null reference exceptions are caught when writing code,
so they are unlikely to occur at runtime.
Unlike JavaScript, Dart supports null safety.
As of Dart 2.12, all types default to non-nullable.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines 646 to 647
By default, Dart _requires_ functions to provide all parameters
as positional parameters.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

// Assign an anonymous function
// to a variable.
var blockFunc =
optionalCallback ?? (int a, int b) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lasse raised it, so I'll count it.

| Meaning | JavaScript operator | Dart operator |
|---------------------------|---------------------|------------------|
| Strict equal | `===` | `==` |
| Abstract equal | `==` | `==` |
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

for evaluating expressions.
Some developers refer to this operator as a ternary operator
as it takes three operands.
Dart has another operator (`[]=`) that takes three operands.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have this updated correctly.

Comment on lines 2713 to +2660
Like JavaScript, Dart has no access modifier keywords:
all class members are public by default.
all class members are public by default.

While private class members are not yet officially
part of JavaScript because they are not part of any
published EcmaScript standard,
a proposal for this has been completed and is ready
to be included in the next publication of the standard.
JavaScript will include private class members in the next
practical revision of the EcmaScript standard.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I checked the reference specifically; privates are in stage 4, which means they will be in the next practical revision. That's why I phrased it as I did.

To indicate that Dart initializes class fields at a later point,
assign the `late` keyword to those class fields.
Those class fields remain non-nullable.
Do this when a variable cannot be observed before being initialized.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. Fixed.

Comment on lines 40 to 41
Dart has one official layout conventions and includes a linter to make
compliance effortless. Dart calls this linter the analyzer.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The analyzer is a lot more than just the linter, providing warnings and errors for misuse of language features,
code completion, actions like "Find usages", "Go to definition", "Fix X", etc.

Suggested change
Dart has one official layout conventions and includes a linter to make
compliance effortless. Dart calls this linter the analyzer.
Dart has official layout and styles conventions and includes
a linter to make compliance effortless.
Support for this and other analysis functionality is provided by the analyzer.

Copy link
Member

@parlough parlough left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for cleaning up this article. Looks good to me.

I left one suggestion around the analyzer introduction.

@atsansone atsansone merged commit 9d67c7c into dart-lang:main Nov 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants