With the soft release of the web interface to Notebooks 2.0, I thought it would be good to hear people’s views on idiomatic approaches to code design in notebooks, as I think the move to vanilla JS exposes interesting issues around code scope and entry points.
In a traditional app, there would typically be single point of entry to start the app, making it clear how the top-level of code design composes lower level functions with appropriate scoping.
In a notebook however, there can be multiple ‘entry points’ via many cells. In Notebooks 1.0, anonymous code blocks were a natural way of providing scoped top-level entry points. For example,
{
const fmt = new Intl.DateTimeFormat(undefined, { dateStyle: "long" });
return fmt.format(new Date());
}
With 2.0, we have to use a different approach. Expressions seem like a natural fit if the entry point is a single line. This works well, for example with a single Plot expression. But if the entry point requires multiple statements, we are forced to take a different approach. For example we could create a code block with a top-level named function and then call it. This also requires us to pass the returned value through display if we want to see it in the cell, and has the disadvantage of adding to the top-level namespace for a potentially one-off function call.
function today() {
const fmt = new Intl.DateTimeFormat(undefined, {dateStyle: "long"});
return fmt.format(new Date());
}
display(today());
There are also arguments for distinguishing the function behaviour from its invocation in separate cells. This would be natural if called more than once in a document. It has the additional advantage of allowing the function call to be an expression, so avoiding the need for display() (but vulnerable to accidentally turning it into a statement with a semicolon):
function today() {
const fmt = new Intl.DateTimeFormat(undefined, {dateStyle: "long"});
return fmt.format(new Date());
}
today()
Or we could make the function anonymous and call it via an IIFE ( IIFE - Glossary | MDN ). Because this is now a one-line expression we don’t need display():
(() => {
const fmt = new Intl.DateTimeFormat(undefined, {dateStyle: "long"});
return fmt.format(new Date());
})()
While it is more compact and directly models the old 1.0 behaviour, it doesn’t feel very beginner-friendly. From experience, the students I teach can easily get lost in the nested and unnested brackets and often don’t have a clear mental model of what’s going on.
What are people’s views on the most idiomatic approaches to multiple entry points in a 2.0 notebook?