Those are good points. The motivations for not introducing new syntax seem quite reasonable. From this point of view, a solution where a regular cell is converted to a “cell with abstracted inputs” seems attractive.
What I’m imagining now is the possibility to have something like the following expression (in this example, I’m taking inspiration from my own actual work on network error correction):
performTest = Notebook.abstract(['numRepairedPackets', 'chart'], ['lossProbability', 'groupSize']]
performTest
would now be a callable, to which replacement inputs can be supplied:
d3.range(0, 100).map((probPerc) => {
const perf = performTest({
lossProbability: probPerc*0.01,
groupSize: 25
});
return {probPerc, perf};
})
// Output:
// [ {probPerc: 0, perf: { numRepairedPackets: ..., chart: ... }]
// , {probPerc: 1, perf: { numRepairedPackets: ..., chart: ... }]
// , {probPerc: 2, perf: { numRepairedPackets: ..., chart: ... }]
// , ...
Another important point of agreement is that side effects such as mutations should remain contained into the “abstracted” evaluation, and never be observable by other cells of the notebook. In my view, triggering updates in the notebook would be very much a bug, not a feature. Rather, it should become a sort of “automatic refactoring”, where a part of the notebook is cut out into an independent instance, with an API dictated by the selected inputs and outputs. The runtime’s knowledge of the dependency graph would be what makes this feasible.
I have to say though, if it becomes necessary to separate the work into two notebooks, one importing the other, it looks to me like there’s a big chance that this feature becomes so inflexible and unergonomic that it may very seldom be worth using. Could someone make an example of applying a collection of replacement inputs, like the above, but with an import-based mechanism?