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

JavaScript usage documentation improvements #3025

Merged
merged 3 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/formatting.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Another MIME type that's generally available is `application/json`. When using t

<img width="511" alt="image" src="https://user-images.githubusercontent.com/547415/223600837-e50a3597-7589-4ed2-add7-254139e8aaec.png">

In the above screen shot, the code coloring in the JSON output is provided by the VS Code notebook renderer for `application/json`.
In the above screen shot, the code coloring in the JSON output is provided by the [VS Code notebook renderer](https://code.visualstudio.com/api/extension-guides/notebook#notebook-renderer) for `application/json`.

## Configuring formatting

Expand Down
89 changes: 86 additions & 3 deletions docs/javascript-overview.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,90 @@
# Using JavaScript in Polyglot Notebooks

JavaScript is one of the languages that's supported by default in Polyglot Notebooks. JavaScript is widely used for visualizations in notebooks since the most popular notebook technologies have long been browser-based. While wrapper libraries for plotting and visualization libraries have become very popular, inclusion of JavaScript and the ability to share data easily from other languages makes it an appealing option to write visualization code directly in the original language these libraries.
JavaScript is one of the languages supported by default in Polyglot Notebooks. JavaScript is widely used for visualization in notebooks since the most popular notebook technologies are browser-based. While many libraries are available in languages such as Python for plotting and visualization, these are usually wrappers around JavaScript libraries. For this reason, inclusion of JavaScript and the ability to share data easily from other languages makes it an appealing option to write visualization code in JavaScript directly.

## Declaring variables

The recommended way to declare JavaScript variables in a notebook differs from the way it's usually done elsewhere.

_**TL;DR**_ Declare your JavaScript variables without using a keyword such as `let`, `const`, or `var`, like this:

```javascript
x = 123;
```

So why is this recommended?

As with other languages that weren't designed for interactive programming, using JavaScript in a notebook has a few special quirks. The difference that people most frequently encounter has to do with how to declare variables.

In JavaScript, variables can be declared in a number of ways, including `let`, `var`, and `const`, as well as without a keyword.

```javascript
let declaredWithLet = 1;
var declaredWithVar = 2;
const declaredWithConst = 3;
declaredWithoutKeyword = 4;
```

Since JavaScript variables are function-scoped, the three keyword-based approaches above will declare variables that can't be referenced outside of the function where they were declared. But the fourth example, declared without a keyword, will work. The following code shows this behavior:

```javascript
const doSomething = async () => {
let declaredWithLet = 1;
var declaredWithVar = 2;
const declaredWithConst = 3;
declaredWithoutKeyword = 4;
}

await doSomething();

try { console.log(declaredWithLet); } catch (e) { console.log(e.toString()); }
try { console.log(declaredWithVar); } catch (e) { console.log(e.toString()); }
try { console.log(declaredWithConst); } catch (e) { console.log(e.toString()); }
try { console.log(declaredWithoutKeyword); } catch (e) { console.log(e.toString()); }
```

This code produces the following output:

```console
ReferenceError: declaredWithLet is not defined
ReferenceError: declaredWithVar is not defined
ReferenceError: declaredWithConst is not defined
4
```

So why does the fourth example work? By not using the `let`, `const`, or `var` keywords with your variable declaration, you're enabling JavaScript variable hoisting to add the variable to the top-level scope. When running in a browser (including the notebook webview), this means the variable will be added to `window`. For this reason, the final line of the above example is equivalent to the following:

```javascript
console.log(window.declaredWithoutKeyword);
```

What does this have to do with Polyglot Notebooks?

The Polyglot Notebooks JavaScript kernel executes your code submissions within an async arrow function, just like the above example:

```javascript
const doSomething = async () => {
// Your code here
Copy link
Contributor

Choose a reason for hiding this comment

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

Just curious: Does some alternate approach exist that would allow us to execute some arbitrary piece of supplied JavaScript within a top-level scope? I am sure there is good reason to select the above approach - but was just curious if there are alternatives and what downsides these alternatives might have.

}

await doSomething();
```

This means that the JavaScript code in each cell is isolated from the others by the same function scoping mechanism. The only way to make variables declared in one cell visible in others is to allow them to be hoisted to the `window` scope, by avoiding the use of the `let`, `var`, and `const` keywords.

## Return values

In C# Script, F#, Python, and a number of other languages, the following is equivalent to a `return` statement:

```csharp
123
```

This is sometimes called a trailing expression and is a feature of many languages. However, it is not supported in JavaScript. If you would like to return a value from a JavaScript cell, you need to write this:

```javascript
return 123;
```

## Loading dependencies

Expand Down Expand Up @@ -61,7 +145,6 @@ configuredRequire = (require.config({

If you'd like to try it out, there's a notebook with a working example using [here](../samples/notebooks/javascript/Plotly%20with%20RequireJS.ipynb).


## Sharing data

Many polyglot notebook workflows use languages such as C#, F#, and SQL to gather and prepare data. Like other .NET Interactive subkernels, the JavaScript kernel allows you to share variables, so you can use JavaScript to plot and visualize your data. Variable sharing in JavaScript works similarly to other languages, using the `#!share` magic command. Here's a simple example, declaring an array variable in C# and then accessing it from JavaScript:
Expand Down Expand Up @@ -96,7 +179,7 @@ When you run this code, you can see that the original C# variable gets overwritt

<img width="503" alt="image" src="https://user-images.githubusercontent.com/547415/211661641-057e29fc-8048-4910-983f-14d8380dca8b.png">

One notable detail is that the type of the new C# variable has changed from the original declaration. It was originally declared as `int[]`, but after sharing the array back from JavaScript, the C# kernel has a variable called `array` which is of type `JsonDocument`. Because the JavaScript kernel runs in a different process from the .NET subkernels, sharing happens via JSON serialization. Complex types shared from JavaScript to .NET are sent as JSON and deserialized using `System.Text.Json`. There are a few simple types that will be shared as their intuitive .NET counterparts.
One notable detail here is that the type of the new C# variable has changed from the original declaration. It was originally declared as `int[]`, but after sharing the array back from JavaScript, the C# kernel has a variable called `array` which is of type `JsonDocument`. Because the JavaScript kernel runs in a different process from the .NET subkernels, sharing happens via JSON serialization. Complex types shared from JavaScript to .NET are sent as JSON and deserialized using `System.Text.Json`. There are a few simple types that will be shared as their intuitive .NET counterparts.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we change the screenshot for the above example to use #!set instead of #!share? That may work better since the sharing is by value by default with #!set (and since there will be a nice error if someone tries to share by reference for JavaScript).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call.


* JavaScript numbers are shared as `System.Decimal`
* JavaScript strings are shared as `System.String`
Expand Down