A "computational notebook" is a type of program that allows mixing formatted text and executable code to create documents with runnable examples. Notebooks are an example of literate programming. A notebook has "cells" which are different text regions. There are commonly three different kinds of cells:
- Code cells contain runnable code.
- Output cells contain the result from the last execution of the associated code cell.
- Markdown cells are display-only and can be used to edit and display richly-formatted (but static) text, including hyperlinks, images, diagrams, and so on.
Project Jupyter, which is probably the most well-known notebook technology, has grown from its origins in scientific and academic environments to become a mainstay in industry for data analysis and data science. Other use cases include interactive documentation and learning materials, troubleshooting guides, and self-guiding automation scripts that can capture structured or visual log output.
Jupyter Notebook is a browser-based notebook UI (or "frontend") from Project Jupyter that can edit and run notebooks using a Jupyter kernel and the .ipynb
file format.
JupyterLab is another browser-based frontend for Jupyter kernels, similar to Jupyter Notebook, but richer and more extensible.
The term "frontend" is often used to describe the UI for a notebook editor. Jupyter Notebook, JupyterLab, nteract Desktop, and the Jupyter Extension for VS Code and GitHub Codespaces are examples of notebook frontends.
A kernel is an execution engine for a notebook. While the notebook UI (or "frontend") is responsible for displaying text, code, and execution results, the kernel is where the actual processing of the code takes place. Kernels are UI-agnostic. They typically run in a different process from the UI, and can often run on a different machine.
In .NET Interactive, there might be several kernels within a single kernel process. We refer to these kernels as "subkernels." Each one represents a stateful unit of computation with a set of capabilities that can include running code, sharing variables, and providing language services (e.g. code completions, diagnostic squiggles, and inline help) for a specific language. A kernel in .NET Interactive does not need to be in its own process. Multiple .NET Interactive subkernels can work together in a single notebook session whether they share a process or are distributed across multiple machines.
A Jupyter kernel is any kernel that implements the Jupyter Message Protocol (JMP). The most commonly-used Jupyter kernel is IPython, an interactive shell for Python, from which Project Jupyter grew.
.NET Interactive is a Jupyter kernel when started in Jupyter mode (using the command line dotnet interactive jupyter
).
.NET Interactive is a .NET tool (dotnet-interactive
) containing a general-purpose engine for interactive programming including, but not limited to, use with notebooks. .NET Interactive has a progammatic interface but no graphical user interface. Various notebook frontends can be used to provide a GUI over .NET Interactive. It includes suport for a number of languages, including C#, F#, PowerShell, and JavaScript, as well as the ability to load support for additional languages such as SQL and Python.
The .NET Interactive kernel (i.e. the dotnet-interactive
.NET tool) is a Jupyter-compliant kernel that can be used with Project Jupyter just like any other kernel. Like other Jupyter kernels, .NET Interactive isn't opinionated about which frontend you use.
The Polyglot Notebooks extension for VS Code isn't required to use the .NET Interactive kernel, but it does provide access to some additional functionality that isn't typical of Jupyter frontends, such as the ability to switch languages (i.e. subkernels) on a per-cell basis.
Polyglot Notebooks is an extension for Visual Studio Code that provides a notebook frontend and related tools for editing and running notebooks with the .NET Interactive kernel.
Polyglot Notebooks is a notebook frontend. It allows you read and write notebook files, run the code in the notebook, and visualize the results.
.NET Interactive is a kernel. It has an API but no user interface. When you press a cell's run button in a frontend such as Polyglot Notebooks, it sends a message to the kernel. The kernel processes the response and sends messages back, including code execution results for the frontend to display.
A subkernel is a concept in .NET Interactive that describes one among many kernels within a single notebook session. Each languages available in a Polyglot Notebook corresponds to a different subkernel. Subkernels can also be added dynamically at runtime, for example when connecting to a data source or a kernel running in a remote process.
A proxy kernel is a concept in .NET Interactive that describes a subkernel that proxies a remote kernel. A proxy kernel can be used locally just like any other subkernel. Proxy kernels allow you to create notebooks that combine kernels running in multiple different processes or on different machines.
One prominent example of a proxy kernel is the JavaScript kernel. The actual implementation is written in TypeScript and runs in a separate process from the .NET Interactive kernel. For example, in the Polyglot Notebooks extension, the JavaScript kernel runs within the same web view that renders the notebook's HTML output. But this kernel can be called programmatically in .NET using the same APIs used to call in-process kernels such as the C# kernel. The proxy kernel serves as the adapter that enables this.
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
var javascriptCode = "console.log('hello from JavaScript!')";
await Kernel.Root.SendAsync(
new SubmitCode(
javascriptCode,
"javascript"));
The .ipynb
file extension is the standard Jupyter notebook format. Despite the name, it's no longer specific to IPython, and can be used for many different languages. It's a JSON-based format and it can store content and metadata for code cells, Markdown cells, and cell outputs, which store the results of code execution for display. Multiple outputs can be stored for each code cell, as long as they differ by MIME type. There are many tools available for diffing, converting, and displaying .ipynb
files. In GitHub,.ipynb
files are displayed using a notebook-style layout.
The Polyglot Notebooks extension can read and write the .ipynb
format. It can also read and write a different format, .dib
. Unlike .ipynb
, which is a presentation-focused document format, .dib
is a scripting format. It does not store outputs, and the raw code of a .dib
can be pasted into a single Polyglot Notebook cell and run directly. This format can contain multiple languages delimited by kernel selector magic commands (e.g. #!csharp
). The .dib
format is also a plain text format, not JSON. It's easier to diff without the need for special tools and the contents don't need any special escaping.
A magic command is a special code command that can be run in a notebook cell, typically using a different syntax than the primary language supported by the kernel.
In IPython, magic commands are prefixed with %
. Since this is not a valid operator in common Python, it allows IPython to easily identify magic commands.
In .NET Interactive, magics are instead prefixed with #!
, since the #
character indicates a comment or preprocessor directive in all of the major .NET languages. Magics must come at the beginning of the line and, while multiple magics can be used in a single cell, a single magic cannot span more than one line. In .NET Interactive, there is no distinction between a "cell magic" and a "line magic".
A "read-eval-print loop", or REPL, is an interactive text-based interface for incrementally creating a program, providing it with input, and seeing its output. While historically one usually interacts with a REPL through a terminal, there are also a number of GUI-based REPLs. Notebooks can be considered an example.
.NET Repl is a .NET tool (dotnet-repl
) that uses the .NET Interactive engine to provide a terminal-based REPL supporting the same general features that you can find in Polyglot Notebooks, including support for combining multiple languages in one session and support for the .ipynb
and .dib
file formats. It also provides some additional features, including the ability to execute notebooks without a UI, allowing for testing notebook files or using them as automation scripts with built-in log capture.
[This is an experimental feature that might be added to the core .NET Interactive product in the future.]
The C# Interactive Window is a REPL window in Visual Studio that lets you write and execute code using the C# Script dialect.
C# Script is a dialect of the C# language that can be used for interactive programming, as well as scripting using the .csx
file format. It differs from common C# in a few key ways, such as its lack of support for namespaces. The library that provides the core Roslyn Scripting functionality, Microsoft.CodeAnalysis.CSharp.Scripting, provides .NET Interactive's default C# functionality.
The F# language can be used for scripting but does not have a separate dialect like C# does. The .fsx
file format is used for scripting in F#.
The F# Interactive Window is a REPL window in Visual Studio that lets you write and execute code using the F# language.
CSI stands for C# Interactive and refers to a command-line C# REPL, csi.exe
, powered by Roslyn Scripting. It is not directly used by .NET Interactive.
FSI stands for F# Interactive and refers to a command line F# REPL, fsi.exe
(also invokable via dotnet fsi
). This tool can be used as an interactive command-ine REPL or to run F# scripts (.fsx
files).
Try .NET is a web application that runs snippets of C# code and shows the result as text output. The code is compiled on the Try .NET server and then executed in the browser using Blazor WebAssembly. It differs from .NET Interactive in that it uses common C#, not C# Script.
The upcoming version of Try .NET reimplements the core Try .NET functionality as a .NET Interactive kernel, bringing common C# to notebooks. This is still a work in progress.
While in a Polyglot Notebook, open the VS Code command palette and select Polyglot Notebook: Focus on Variables View
. This will open the Panel to the POLYGLOT NOTEBOOK: VARIABLES
tab, which displays the variables in all of the different loaded kernels.
From within a running notebook, it's currently not possible to directly access the outputs. The data used to populate them is transient and the frontend, not the kernel, is responsible for writing the file. They outputs are stored in the .ipynb
file though and can be read from there if you have the path to the file. The .NET Interactive packages include a library for parsing and writing various file formats including .ipynb
and .dib
: Microsoft.DotNet.Interactive.Documents.
Yes! In Codespaces, the Polyglot Notebooks extension can be loaded from the VS Code Marketplace.
While the Polyglot Notebooks extension can be loaded in github.dev, it can only be used to view notebooks. Code execution is not available currently in the browser-only environment.
You can use .NET Interactive as a standard Jupyter kernel and use it with Jupyter Notebook, JupyterLab, and other Jupyter frontends. Instructions for registering it as a Jupyter kernel can be found here.
You can use the #r nuget
directive within a C# or F# cell to load NuGet packages into a notebook. You can read more details here.
Yes. Custom package feeds are supported via the #i nuget
directive. You can read more details here.
This is not directly supported by .NET Interactive, but you can authenticate to a NuGet feed by storing a PAT in your user-level nuget.config. You can read more about this approach here.
You can directly load a .NET assembly in a C# or F# cell by using the #r
compiler directive:
#r "/path/to/assembly.dll"
Directly running code from the C# or F# project isn't currently supported, but the scenario is under consideration and feedback is welcome.
There are a couple of approaches that might work, depending on what you're trying to do.
-
You can run code from a specific file directly using the
#!import /path/to/file
magic command, which can understand a number of different file formats. The contents of the file will be run directly. If the file is a.cs
file it will attempt to run it using C# Script, and the dialect differences might prevent the file from compiling. (For example, since C# Script doesn't support namespaces, the file will not be able to include them.) F# files (.fs
) tend to be more likely to be runnable in this way. -
You can call into assemblies built from your project code by using the
#r
compiler directive in a C# or F# cell.
You can load a JavaScript library using require
.
JavaScript package managers are not currently supported, however.
You can define new magic commands using the .NET Interactive extension APIs.
Not all kernels support variable sharing, but for those that do, you can use the #!share
magic command to pull a variable from one kernel into another.
There are a few ways to display values, and some differences depending on which language you're using.
The most universal approach is to write to the console, though this will result in plain text output that might be less interesting in some cases. (Return values and display values, described below, can be richly formatted using .NET Interactive formatters.)
In C# and F# you can call Console.WriteLine
.
In PowerShell, an unpiped expression will produce console output by default, so all you have to do is this:
In JavaScript, you can call console.log
.
You can also return a value in order to display it. C# Script and F# have a similar syntax for this, which is a trailing expression. This is the common syntax for returning a value in F#. In C# Script, it's a syntax that is not valid in common C#.
Since a return statement is only valid once in a given submission, .NET Interactive also provides an extension method that can be used to display any object. This can be called more than once in a single submission.
In PowerShell, you can use Out-Display
to pipe values through the display helper.
The formatter APIs allow extensive control over the way that outputs (including notebook cell return values and values displayed using the Display
method) are displayed. All type-specific formatting, as well as the differences between notebook outputs (typcally HTML) and REPL outputs (typically plain text), is powered by these APIs.
You can read more about the formatter APIs here.
There are a few ways to prompt a user for input in a notebook.
You can prompt for input using a token such as @input:prompt_name
within any magic command:
(As you can see from the file picker in the screenshot, input prompts can be type-specific.)
There is also an API that can be called directly to prompt users for input. Here's an example in C#:
using Microsoft.DotNet.Interactive;
var input = await Kernel.GetInputAsync("Pick a number.");
When you run this code in Polyglot Notebooks (as shown in the screenshot below), a text input prompt appears at the top of the Visual Studio Code window.
You can read more about input prompts here.
User input prompts are a good way to provide secrets to a notebook without risk of storing them in the file. Similar to the
using Microsoft.DotNet.Interactive;
var input = await Kernel.GetPasswordAsync("Pick a number.");
When you run this code in Polyglot Notebooks (as shown in the screenshot below), an input prompt appears at the top of the Visual Studio Code window. When the user types into this prompt, the text is masked.
When you run a cell, the run button (Polyglot Notebook: Restart the current notebooks kernel
command from the command palette.
Yes, you can use the .NET Interactive APIs to send code to any kernel by name.
Here's an example in C#:
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
var code = @"Console.WriteLine(""Hello!"");";
await Kernel.Root.FindKernelByName("csharp")
.SendAsync(new SubmitCode(code));
The .NET REPL has a number of features relating to command line automation with notebooks. The GitHub project page has more details.
[This is an experimental feature that might be added to the core .NET Interactive product in the future.]
One approach to automated testing of notebooks is to use .NET REPL. The following example shows how to run a notebook headlessly and output a .trx
file containing the results:
dotnet repl --run /path/to/notebook.ipynb --output-format trx --output-path /path/to/results.trx --exit-after-run
More details can be found on the GitHub project page.
[This is an experimental feature that might be added to the core .NET Interactive product in the future.]
Yes. You can use the #!import
magic command to load and run a number of different file types within a notebook, including .ipynb
and .dib
. Both of these file formats support polyglot, so notebooks imported this way can include cells in various languages.
You can also use #!import
to run single-language files within a notebook, including .cs
, .csx
, .js
, and .ps1
files.
You can read more about #!import
here.
Yes. The .NET Interactive extensibility APIs allows for NuGet packages to add new subkernels at runtime. This is how SQL and KQL support are implemented.
Yes, in Polyglot Notebooks you can programmatically add a new cell by sending the SendEditableCode
command.
using Microsoft.DotNet.Interactive;
using Microsoft.DotNet.Interactive.Commands;
var command = new SendEditableCode(
"csharp",
"Console.WriteLine(\"Hello!\");");
var input = await Kernel.Root.SendAsync(command);
When you send this command, a new cell will be appended to the notebook with the specified content and selected kernel:
This command is not currently supported in other notebook frontends such as JupyterLab.
It is common for a notebook's kernel to get into a stuck state. Maybe you tried to load too much data or accidentally ran code containing an infinite loop. This happens often enough that a way to restart the kernel is a feature of most notebook providers.
In Polyglot Notebooks, you can restart the kernel by running the Polyglot Notebook: Restart the current notebook's kernel
command from the Command Palette. Note that after you do this, you'll need to rerun the notebook's cells, including reloading packages using #r
.
Sometimes VS Code updates have been applied or are pending and the Polyglot Notebooks extension needs to be updated. If things aren't working, here are a few things to check.
If you see the following badge on the settings icon in the lower left corner of VS Code, it means there might be an update pending:
When you click it and see a Restart to Update
message in the menu, then VS Code needs an update.
You might also see, including after a VS Code update has been applied, that the Polyglot Notebooks extension requires a reload.
If you're still seeing issues running code after checking for updates, please open an issue.
This error indicates that a call to a .NET Interactive formatting API such as Display
, or in some cases #!share
or #!set
, failed because formatting the specified object using the requested MIME type isn't available. You can add formatters for additional MIME types using the formatter registration APIs. More details about these APIs can be found here.
The error is shown because a formatted value from the .NET Interactive kernel has been formatted using a MIME type that VS Code doesn't know how to render. VS Code can only render notebook outputs in MIME types for which there's an installed notebook renderer. Notebook renderers are independent of the kernel and can be installed from the Visual Studio Marketplace.