Authors Jingjing Zhao, Xinyi Zhang, Yee-Jian Tan, Peter Jung, Zachary Chua, Thomas Tan, Martin Henz,
License CC-BY-SA-4.0
A Stepper for a Functional JavaScript Sublanguage
Martin Henz
Thomas Tan
Zachary Chua
Peter Jung
Yee-Jian Tan
Xinyi Zhang
Jingjing Zhao
henz@comp.nus.edu.sg,{e0177215,e0543984,e0552326,e0325774,e0424857,e0424694}@u.nus.edu
National University of Singapore (NUS)
Figure 1. The stepper, showing the step just after the third call of factorial, written in JavaScript.
Abstract Source ğ2 programs. To support the learner in adopting this
The first two chapters of the introductory computer science mental model, we built an algebraic stepperÐa tool for visu-
textbook Structure and Interpretation of Computer Programs, alizing the evaluation of Source ğ2 programs according to
JavaScript Edition (SICP JS), use a subset of JavaScript called the model. As a sublanguage of JavaScript, Source ğ2 differs
Source ğ2. The book introduces the reduction-based łsubsti- from other purely functional programming languages by us-
tution modelž as a first mental model for the evaluation of ing a statement-oriented syntax, with statement sequences,
return statements, and block-scoped declarations. For the
Permission to make digital or hard copies of part or all of this work for
purpose of this tool description, we distill these distinguish-
personal or classroom use is granted without fee provided that copies are ing featuresÐalong with explicit recursionÐinto a Source ğ2
not made or distributed for profit or commercial advantage and that copies sublanguage that we call Source ğ0, and focus on a stepper for
bear
This this notice
work is and the full under
licensed citation aon Creative
the first page. Copyrights
Commons for third-
Attribution- this language. We formalize the substitution model of Source
party components
ShareAlike of this work
4.0 International must be honored. For all other uses, contact
License.
ğ0 as a lambda-calculus-style reduction semantics that han-
the owner/author(s).
dles explicit recursion by term graph rewriting and faithfully
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
© 2021 Copyright held by the owner/author(s).
implements the JavaScript specification, when restricted to
ACM ISBN 978-1-4503-9089-7/21/10. that language. Our implementation of the stepper represents
https://doi.org/10.1145/3484272.3484968 term graphs by persistent data structures that maximize
71
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, Xinyi Zhang, and Jingjing Zhao
sharing and enable random access to all steps. This work prog ::= stmt . . . program
presents the first reduction-based semantics for a JavaScript
stmt ::= function name
sublanguage and the first algebraic stepper for a language
with return statements and block-scoped declarations. The ( names ) block function decl.
tool supports the learner with step-level explanations, redex | return expr ; return statement
highlighting, and function-level skipping and can also be | expr ; expression st’ment
used for teaching the applicative-order-reduction lambda
calculus. names ::= 𝜖 | name ( , name ) . . . parameters
CCS Concepts: · Software and its engineering → Inte- block ::= { prog } function body
grated and visual development environments; · Applied expr ::= number | true | false
computing → Computer-assisted instruction; · Theory
| undefined literal expression
of computation → Lambda calculus.
| name name expression
Keywords: programming environments, education, function-
| expr bin-op expr binary op. comb.
al languages, lambda calculus, semantics, JavaScript, stepper,
term graph rewriting | expr ( exprs ) function appl.
ACM Reference Format: | expr ? expr : expr conditional expr.
Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, | ( expr ) parenthesized expr.
Xinyi Zhang, and Jingjing Zhao. 2021. A Stepper for a Functional
JavaScript Sublanguage. In Proceedings of the 2021 ACM SIGPLAN bin-op ::= + | - | * | / | ===
International SPLASH-E Symposium (SPLASH-E ’21), October 20, | > | < | >= | <= binary operator
2021, Chicago, IL, USA. ACM, New York, NY, USA, 11 pages. https:
//doi.org/10.1145/3484272.3484968 exprs ::= 𝜖 | expr ( , expr ) . . . argument expr’s
1 Introduction Figure 2. Source ğ0 in Backus-Naur Form [18]; bold font
for keywords, italics for syntactic variables, 𝜖 for nothing,
A hallmark of the introductory computer science textbook
𝑥 | 𝑦 for 𝑥 or 𝑦, and ( 𝑥 ) . . . for zero or more repetitions of 𝑥.
Structure and Interpretation of Computer Programs (SICP, [1])
is its functional-programming-first approach. Computation
is introduced as a step-by-step process of simplification and
semantics. Section 5 outlines how this semantics is imple-
function unfolding, reminiscent of familiar mental models in
mented in our stepper and how the tool is integrated in
mathematical calculation and equation solving. In the book,
the Source Academy, our online environment for teaching
this process is called the substitution model, and can in princi-
SICP JS [23]. Section 6 gives example sessions of the step-
ple explain the evaluation of all programs in its first two chap-
per to illustrate its basic features. Section 7 introduces the
ters. The JavaScript adaptation of SICPÐSICP JS [2]Ðuses
advanced feature of function skipping, demonstrates its use
JavaScript for all programs instead of the language Scheme
and discusses its implementation. Numerous stepper tools
used in the first two editions of SICP. JavaScript employs
and systems for designing and implementing such tools are
a statement-oriented syntax, which is the syntactic style
presented in the literature. In Section 8, we compare the most
of most mainstream languages as of 2021, including Java,
closely related systems to our approach, before concluding
Python, and C, rather than Scheme’s expression-oriented
and discussing planned extensions in Section 9.
syntax. The most significant change in the first two chapters
The stepper is implemented in an open-source program-
of SICP JS, compared to SICP, is the use of return statements
ming environment custom-built for first-year students. The
and block-scoped declarations.
beginning of Section 6 explains how to use the system and
While teaching Computer Science first-year students us-
how to navigate to the stepper.
ing SICP JS, we saw the need for a tool that explains the
evaluation of programs in the first two chapters accord-
ing to the substitution model. Existing JavaScript imple- 2 Syntax of Source §0
mentations, including the browsers’ JavaScript debugging To focus on the distinguishing features of the stepper, Fig-
tools, are not suitable for this task, because the substitu- ure 2 specifies a Source ğ2 sublanguage that we call Source
tion model radically differs from their underlying computa- ğ0. We limit the discussion in this section and Sections 3
tional models. Section 2 specifies Source ğ0, a sublanguage and 4 to Source ğ0 to avoid excessive formalism in this tool
of JavaScript that includes the most important features that description. The full language Source ğ2Ðspecified in [16]Ð
distinguish JavaScript from Scheme. Section 3 specifies its adds blocks as statements, constant declarations, conditional
formal, lambda-calculus-style reduction semantics, and Sec- statements, lambda expressions, unary operators, more bi-
tion 4 describes the notion of substitution employed in the nary operators, strings, and pairs as primitive data structures.
72
A Stepper for a Functional JavaScript Sublanguage SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
A full Source ğ2 stepper is implemented in the Source Acad- that 𝑝𝑛 → 𝑞. If 𝑝𝑛 is of the form 𝑣;, we call 𝑣 the result
emy and described in [9]. of the program evaluation. If 𝑝𝑛 is the empty sequence of
In the Source languages, the bodies of functions are blocks, statements, we declare the result of the program evaluation
which may contain return statements and expression state- to be the value undefined. Reduction can get łstuckž; the
ments in any position, as illustrated by the following (some- result can be a program that has neither of these two forms,
what contrived) example program, which we will call f-apply. which corresponds to a runtime error, for example when a
function is used as an operand of the arithmetic operation *.
function f(x) { 17; return x + 1; 57; }
2 + 3; f (4 * 5) - 6; 3.1 Operator Combinations, Conditionals, and
The first two chapters of SICP JS adhere to purely func- Sequences
tional programming (no assignment, stateful operations or The rules Op-Comb-Intro (OCI1, OCI2), Op-Comb-Red (OCR),
loops), and rely on (mutual) recursion as the central means Cond-Intro (CI), and Cond-Red (CR1, CR2) implement the
of control flow. In the following example program, which we binary operator combinations and conditional expressions
will call even-odd, the declarations of the mutually recursive of Source ğ0.
functions even and odd are followed by the application of 𝑒 1 → 𝑒 1′
even to the literal 5. [OCI1]
𝑒 1 bin-op 𝑒 2 → 𝑒 1′ bin-op 𝑒 2
function even (n) { 𝑒 2 → 𝑒 2′
return n === 0 ? true : odd (n - 1); [OCI2]
𝑣 bin-op 𝑒 2 → 𝑣 bin-op 𝑒 2′
}
function odd (n) { 𝑣 is result of 𝑣 1 bin-op 𝑣 2
[OCR]
return n === 0 ? false : even (n - 1); 𝑣 1 bin-op 𝑣 2 → 𝑣
} 𝑒 1 → 𝑒 1′
[CI]
even (5); 𝑒 1 ? 𝑒 2 : 𝑒 3 → 𝑒 1′ ? 𝑒 2 : 𝑒 3
In JavaScript (and Source), the scope of a function declara- [CR1] [CR2]
tion is the surrounding block, or the whole program if there true ? 𝑒 1 : 𝑒 2 → 𝑒 1 false ? 𝑒 1 : 𝑒 2 → 𝑒 2
is no surrounding block as in this case. As in JavaScript, we Our proof trees identify in square brackets each rule being
stipulate that function declarations are moved (łhoistedž) to applied. The base case at the top of a tree is always a reduc-
the beginning of the block or program, as a preprocessing tion rule ...-Red rule (...R...).
step.
[OCR]
1 + 2→3
3 Reduction [OCI1]
3 === 1 + 2 → 3 === 3
In this section, we define a reduction relation →∗ that de- [CI]
3 === 1 + 2 ? 17 : 42 → 3 === 3 ? 17 : 42
scribes the evaluation of Source ğ0 programs. The stepper
The program component where a reduction rule can be
visualizes the evaluation process. The reduction of the even-
applied is called a redex. JavaScript semantics prescribes that
odd program above will include the following steps; the exact
there is at most one redex in any program with only one
shapes of even and odd during reduction are explained in
applicable rule, and therefore → must be a function. The re-
Section 3.2.
dexes of our example proof trees of Section 3 are highlighted
even-odd with a gray background.
→∗ even(5); →∗ 5 === 0 ? true : odd(5 - 1);
Expression-Statement-Intro (ESI): An expression statement as
→ odd(5 - 1); →∗ 4 === 0 ? false : even(4 - 1);
∗
first statement in a statement sequence can be reduced if the
→∗ odd(1 - 1); →∗ 0 === 0 ? false : even(0 - 1);
expression can be reduced.
→∗ false;
expr → expr ′
We will define the one-step reduction function → as a [ESI]
expr; prog → expr ′; prog
partial function from programs/statements/expressions to
programs/statements/expressions, and →∗ as its reflexive First-Statement-Red (FSR), First-Statement-Intro (FSI): The
transitive closure. The one-step reduction function is the JavaScript specification [15] categorizes statement sequences
least relation that meets all rules given in this section using statically as value-producing (in our case: sequences contain-
Gentzen-style. A value is a literal expression or a recursive ing expression or return statements) and not value-producing
function definition (introduced in Section 3.2), and values (in our case: sequences of declarations). A statement 𝑣; in
are denoted with the letter 𝑣 in the following. front of a value-producing statement sequence is discarded,
A reduction is a sequence of programs 𝑝 1 → 𝑝 2 → · · · → and a statement 𝑣; in front of a not value-producing state-
𝑝𝑛 , where 𝑝𝑛 is not reducible, i.e. there is no program 𝑞 such ment sequence is retained.
73
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, Xinyi Zhang, and Jingjing Zhao
The rule FSI explains why the final value of a program function odd (n) { (𝜇 even .( n) => {
is retained, the remaining not value-producing statement return n === 0 return n === 0
sequence being the empty sequence in this case. In Source ğ2, ? false ? true
the FSI rule also explains why the value of the program : (𝜇 even .( n) => { : (𝜇 odd .( n) => {
return n === 0 return n === 0
1; const x = 0; ? true ? false
: odd (n - 1); : (𝜇 even .( n) => {
is 1 and not undefined. }) return n === 0
(n - 1); →FDR
? true
prog is value-producing }
[FSR] : odd (n - 1);
v; prog → prog (𝜇 even .( n) => { })
return n === 0 (n - 1);
? true })
prog → prog ′ and prog is not value-producing : odd (n - 1);
[FSI] (n - 1);
v; prog → v; prog ′ }) })
(5); (5);
Two steps from the reduction of f-apply illustrate some of Function-application-Red (FR): JavaScript’s specification [15]
the rules introduced so far; the shape of fun is explained in stipulates applicative-order reduction and thus, the applica-
Section 3.2. tion of a function can be reduced, only if all arguments are
[OCR] values. Since all function declarations substitute the function
2 + 3→5 name by a 𝜇-term, we combine 𝛽-reduction of the lambda
[ESI] calculus with the 𝜇-rule, stated in [3] in the context of the
2 + 3; fun(4 * 5) - 6; → 5; fun(4 * 5) - 6;
𝜆𝜇-calculus: 𝜇𝑥 .𝑍 → 𝑍 [𝑥 ← 𝜇𝑥 .𝑍 ]
expr = 𝜇 𝑓 .( 𝑥 1 . . . 𝑥𝑛 ) => block
[FSR] expr(𝑣 1 . . . 𝑣𝑛 ) → block[𝑥 1 ← 𝑣 1 ]. . .[𝑥𝑛 ← 𝑣𝑛 ] [𝑓 ← expr]
5; fun(4 * 5) - 6; → fun(4 * 5) - 6;
This rule [FR] introduces a block expression, the second new
3.2 Function Declaration and Application kind of expressions that is not part of Source ğ0:
Function-Declaration-Red (FDR): Function declarations are expr ::= block block expression
reduced as follows.
The result of the second step in the reduction of even-odd is
[FDR] further reduced with FR (with no free occurrences of even to
function f (names) block prog be unfolded), and then with BER3 (which we will introduce
→ prog[f ← 𝜇 𝑓 .(names) => block] in Section 3.3):
{
Here, 𝜇-terms [3] are used to represent recursive function
return 5 === 0 5 === 0
definitions. They are not part of Source ğ0, but appear during
? true ? true
reduction.
: (𝜇 odd .( n) => { : (𝜇 odd .( n) => {
return n === 0 return n === 0
expr ::= 𝜇 name.( names ) => block recursive funct. def.
? false ? false
: (𝜇 even .( n) => { : (𝜇 even .( n) => {
Every occurrence of name in block represents the recursive
return n === 0 return n === 0
function definition itself, and thus can be seen as a cycle in
→FR,ESI ? true →BER3,ESI ? true
the expression. Here, 𝑡 [𝑥 ← 𝑠] denotes the capture-avoiding
: odd (n - 1); : odd (n - 1);
substitution of 𝑠 for 𝑥 in 𝑡 (details in Section 3.2), according
}) })
to the following scoping rules. In JavaScript [15], the scope
(n - 1); (n - 1);
of a function declaration is the surrounding block or the
}) })
whole program if there is no surrounding block, and the
(5 - 1); (5 - 1);
scope of parameters is the function body. The scope of name
};
in 𝜇 name.( names ) => block is block.
The first two reduction steps for the even-odd program Function-application-Intro (FI1, FI2): The application intro-
illustrate how function declarations give rise to cyclic recur- duction rules stipulate strict, left-to-right reduction of the
sive function definitions. components of function applications, following JavaScript’s
even-odd →FDR specification [15].
74
A Stepper for a Functional JavaScript Sublanguage SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
expr → expr ′ the component to be substituted into a name binder con-
[FI1] tains one of the binder’s names as a free name. In Source ğ0,
expr ( exprs ) → expr ′ ( exprs )
name binders are function declarations, whose parameters
expr → expr ′ are binding, blocks, whose function declarations are binding,
[FI2] and 𝜇-terms of the form 𝜇 name.(names) => block, where
𝑣 ( 𝑣 1, . . . , 𝑣𝑖 , expr, . . . ) → 𝑣 ( 𝑣 1, . . . , 𝑣𝑖 , expr ′, . . . )
name and all names in names are binding. Capture-avoiding
3.3 Block Expressions substitution of an expression e for a name 𝑦 in a 𝜇-term is
The rule FR gives rise to block expressions, which are blocks defined as follows:
that appear as expressions as a result of the unfolding of a
(𝜇z.(names) =>
function application expression.
block[x ← 𝑧]) [𝑦 ← 𝑒]
if x occurs free in 𝑒
Block-Expression-Intro/Reduce (BEI, BER1, BER2): A block
′
expression is reducible if its body is reducible. A block ex- (𝜇x.(names) => block) (𝜇z.(names ) =>
pression whose body is the empty statement sequence or = block[n ← 𝑧]) [𝑦 ← 𝑒]
[𝑦 ← 𝑒]
only contains a single value statement reduces to the value
if 𝑛 ∈ names occurs free in 𝑒
undefined.
𝜇𝑥 .(names) =>
prog → prog ′
[BEI] [BER1]
block[𝑦 ← 𝑒]
{ } → undefined
{ prog } → { prog ′ } otherwise
[BER2] where 𝑧 is fresh, neither being 𝑥 nor occurring in names,
{ 𝑣 ; } → undefined
block or 𝑒, and where names ′ is the result of replacing 𝑛 by 𝑧
BER1 and BER2 reflect that in JavaScript, a function returns
in names. The replacement of 𝑥 or 𝑛 by 𝑧 prior to substitution
the value undefined, if the reduction of the function body
in the first two cases is called alpha-renaming.
does not encounter a return statement, for example:
The rules for avoiding capturing when applying substi-
tution to function declarations and blocks given in [9] sim-
function g(x) { x; } g(5);
ilarly avoid the capturing of free names in the substituted
→∗ { 5; };
components by parameters and local names of the function
→BER2 undefined;
declarations and blocks.
Block-Expression-Red (BER3): A block expression whose body
starts with a return statement reduces to the return expres- 5 Implementation and Integration
sion of the return statement and ignores any subsequent The Source Academy is web-based, written in TypeScript us-
statements. ing React. The TypeScript sources are compiled to JavaScript
and bundled into a web application that gets served to the
[BER3] browser when the learner visits the public website of the
{ return 𝑒𝑥𝑝𝑟 ; stmt . . . } → 𝑒𝑥𝑝𝑟
Source Academy [14]. When the learners select the stepper,
Each line in the following reduction of the example f-apply their programs are not executed using the default Source
in Section 2 is annotated by all rules involved in the reduc- language implementation but by invoking the stepper com-
tion, and the redexes of ...-Red rules are highlighted. ponent of a collection of open-source implementations of the
Source languages that also includes transpilers to JavaScript,
f-apply →FDR 2+3; (𝜇f.(x) => { . . . })(4 * 5) - 6; interpreters, compilers and virtual machines.
→OCR,ESI 5; (𝜇f.(x) => { . . . })(4 * 5) - 6; Upon invoking the stepper, the program is parsed into
→FSR,ESI (𝜇f.(x) => { . . . })(4 * 5) - 6; an abstract syntax tree (AST) following the ESTree spec of
→OCR,FI2,OCI1,ESI (𝜇f.(x) => { . . . })(20) - 6; the JavaScript syntax [13]. Instead of representing 𝜇-terms
→FR,OCI1,ESI { 17; return 20 + 1; 57; } - 6; explicitly, we decided to implement back-references to en-
→FSR,BEI,OCI1,ESI { return 20 + 1; 57; } - 6; closing function declarations simply by cyclic references
→BER3,OCI1,ESI (20 + 1) - 6; to their nodes in the AST, which as a result is more aptly
→OCR,ESI 21 - 6; called an abstract syntax graph (ASG). The syntax graph of
→OCR,ESI 15; the given program is repeatedly reduced until the result is
reached. The ASGs of all reduction steps are stored in an ar-
4 Capture-Avoiding Substitution ray, and are converted back into program text in the frontend
As in the lambda calculus, we define substitution by apply- of the Source Academy whenever necessary. The learner can
ing substitution rules recursively, following the structure of access the steps by dragging a slider or using hotkeys. We ex-
the program. It avoids capturing by alpha-renaming, when tend the ESTree spec for our purpose by allowing cycles and
75
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, Xinyi Zhang, and Jingjing Zhao
coreferences, and by adding block expressions. In the inter- destructive operations on pairs and thus, the evaluation of
face, we display 𝜇𝑥 .expr simply as 𝑥, so the reduction steps their programs cannot be explained with a reduction-based
for the even-odd example appear as shown in the beginning model.
of Section 3. After clicking on the stepper icon (see circle highlighted
Our implementation of the reduction rules of Section 3 in white), the evaluation switches to reduction and the REPL
represents ASGs as persistent data structures. In the terminol- is replaced by the stepper. The learner can set a limit for the
ogy of [11], the ASG is partially persistent during reduction: number of steps (with a default value of 1000), to handle long
all previous versions of the ASG can be accessed but only reductions. Figure 1 above the abstract shows the stepper
the newest version is being modified (constructed). Careful after the learner moves the slider to Step 22. Each reduction
implementation of reduction and substitution results in sig- is shown in two steps: before (even step number) and after
nificant sharing of data structures across the elements of the (odd step number) the reduction. The redex is highlighted in
array of ASGs. As typical for persistent data structures, a red in even-numbered steps and the result of reducing the
reduction step copies only the path from the root node of the redex is highlighted in green in odd-numbered steps.
program to the redex. Our implementation of substitution Each step displays an explanation text below the program.
keeps track of the already processed nodes of the syntax
graph to avoid any repeated application of recursive substi-
tution rules to nodes of cyclic ASGs. Substituted expressions
are shared across the replacements sites, introducing corefer-
ences in the resulting ASG. Coreferences within and sharing
between ASGs enable the greedy generation and retention
in browser memory of all steps of the learner program.
The run time of the stepper is not perceptible for most
textbook examples and learner programs. In our experience Step 22 in Figure 1 shows the program just after the third
of using the stepper in an introductory computer science call of factorial. Step 36 below shows the program just
course in NUS, the performance of the stepper is more than before the final recursive call of factorial, and the deferred
sufficient. An adjustable step limit allows the learner to play multiplications that have accumulated during the previous
with non-terminating programs. As a gauge of the stepper’s recursive calls.
performance, the evaluation of the following infinite loop
takes 12 seconds with a step limit of 50,000, on a 2.2 GHz
MacBook Pro using the Google Chrome browser Version 20.
function f () { return f (); } f ();
6 Example Sessions The last step shows the result of factorial(5).
The web-based IDE of the Source Academy lets the learner
enter their programs in an editor on the left. After pressing
łRunž, the program is evaluated and the result is displayed
in a read-eval-print-loop (REPL) on the right, which allows
the learner to test and explore the functions declared in the
program.
To save space, we clipped off the display of the explanation
in the images below.
6.1 Stepping through the Program even-odd
We show how the stepper visualizes the evaluation of the
even-odd example discussed in Section 3.2.
Selection of the Source language (Source ğ1, ğ2, ğ3, or ğ4,
see oval highlighted in white) restricts the evaluator to only
accept programs of the selected JavaScript sublanguage. The
stepper is available when one of the purely functional Java-
Script sublanguages, Source ğ1 or Source ğ2, is selected. The
languages Source ğ3 and Source ğ4 include assignment and
76
A Stepper for a Functional JavaScript Sublanguage SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
The functions even and odd are declared and substituted
into the rest of the program, where even is applied to 5. The
stepper highlights the next expression to be reduced.
After reducing the declaration function even(n) {...}
with FDR in Section 3.2, the name even in even(n - 1); and
even(5); refers to a 𝜇-term 𝜇 even.expr, which the stepper
represents by just printing even to keep the program read- The functions even and odd call each other recursively
able. until the base case odd(0) is reached. This application is
replaced with the return expression of odd, in which the
condition 0 === 0 is now reduced to true,...
...finally returning the consequent expression false.
6.2 Stepping through the f-apply Program
When a recursive function definition is applied whose We illustrate how the stepper visualizes the reduction of the
body consists of a single return statement as in even(5), the f-apply example given at the end of Section 3.3.
stepper combines FR in Section 3.2 and BER3 in Section 3.3
into a single step. The result in this case is a conditional ex-
pression in which the condition 5 === 0 is reduced to false
with CI in Section 3.1, returning the alternative expression
odd(5 - 1) using CR2 in Section 3.1, which is reduced to
odd(4).
The function f is declared and substituted into the rest of
the program. After reducing the declaration function f(x)
{...} with rule FDR in Section 3.2, the name f refers to a 𝜇-
term in f(4 * 5) - 6;, which the stepper displays as just f.
Similarly, the application of the recursive function defini- The expression statement 2 + 3; is reduced to 5; using ESI,
tion odd is replaced with the return expression of its body, in but 5; is discarded using FSR, both in Section 3.1.
which the condition 4 === 0 is reduced to false, returning
the alternative expression even(4 - 1), which is reduced
to even(3).
77
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, Xinyi Zhang, and Jingjing Zhao
The application of f is replaced with the body of f using FR
in Section 3.2, in which all occurrences of x are substituted
by 20. Note the highlighted result showing a block expres-
sion within an operator combination, which is not part of
the JavaScript syntax.
As with 5; in Step 6, the statement 17; is discarded due to
FSR.
The block expression’s first statement is now a return state- At Step 10, the functions declarations are all substituted into
ment with the return expression 20 + 1. The block is re- the function expression of the final application sqrt(5)...
duced to the return expression due to BER3 in Section 3.3.
The remaining reduction steps are completed and the pro-
gram is reduced to 15;.
...which unfolds the body of the recursive function definition
sqrt, resulting in another example of a non-JavaScript block
expression.
7 Skipping to Function Applications
The number of steps even for relatively simple textbook
programs can quickly reach several thousand. In addition to
the slider and hotkeys for moving forward and backward by
one step, we decided to support function-level skipping. When
the evaluation of a program reaches a function application
of a function 𝑓 , the learner has in general four options:
• move to the previous step (if there is one), using a <
button,
• move to the next step (if there is one), using a > button,
• take a łbig stepž backward by skipping to the previous
application of 𝑓 (if there is one), using a ń button, or
• take a łbig stepž forward by skipping to the next ap-
plication of 𝑓 (if there is one), using a ż button.
We illustrate this feature using the example of Newton’s
method for computing square roots from Section 1.1.8 of
SICP JS. After running the program with the stepper, it shows Reduction continues to the application of sqrt_iter at
the entire program as łStart of evaluationž. Step 20.
78
A Stepper for a Functional JavaScript Sublanguage SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
Using only the arrow button > or hotkeys, the learner would
need to click through a large number of steps...
Similarly, the learner may decide to trace the calls of the
improve function by repeatedly pressing the ż button, start-
ing in Step 42.
...in search for the next call of sqrt_iter. Instead of carry-
ing out small reduction steps, the learner can skip from the
application of sqrt_iter in Step 20...
...to the next application of this function, by pressing the ż
button, highlighted above with a white circle, skipping to
Step 52. ...followed by sqrt_iter(improve(2.333)); in Step 102
and sqrt_iter(improve(2.238)); in Step 132 (not shown).
The last call of the function is_good_enough...
Repeated skipping allows the learner to quickly see the
reduction steps that involve sqrt_iter.
...returns true,...
79
SPLASH-E ’21, October 20, 2021, Chicago, IL, USA Martin Henz, Thomas Tan, Zachary Chua, Peter Jung, Yee-Jian Tan, Xinyi Zhang, and Jingjing Zhao
√
...and the reduction terminates with an approximation of 5 These implementations are based on term (tree) rewriting
to the required accuracy as the result. and not on graph rewriting and do not handle explicit recur-
sion, which is necessary for a direct implementation of our
reduction semantics. They share with our stepper and with
Hat that the intermediate results of a reduction are explicitly
generated and stored. As a web-based interactive system
written in TypeScript using React, Lambdulus has a similar
general system architecture as the Source Academy.
Our stepper performs before/after redex highlighting as
Our implementation of this feature makes use of the shar- do LambdaLab and Lambdulus. A survey of functional pro-
ing of syntax graphs in the array of graphs. The buttons ń gram visualization systems in [24] lists several earlier im-
and ż are rendered as clickable at a function application if a plementations that share this feature, including the Scheme
previous/following application step is found, whose function Stepper [10].
expression is the same syntax graph (using pointer equality) Chang introduces the JavaScript variant Javascripty [6]
as the function expression of the function application where in his programming languages course, the only JavaScript-
the search started. related small-step reduction-based semantics that we are
aware of. Javascripty is not designed as a JavaScript sublan-
8 Discussion of Related Work guage. It deviates from JavaScript in the scoping of constant
Algebraic steppers have been developed for many functional declarations and supports only a restricted form of return
programming languages, including Scheme [10], Racket [20], statements. As opposed to Javascripty [6], our approach
Lazy Racket [7], and Haskell [8]. The Scheme, Racket, and handles JavaScript’s statement-oriented syntax, allows re-
Lazy Racket steppers generate a stack of continuations at turn statements to occur anywhere in a statement sequence,
run time by instrumenting the learner’s program with łcon- and covers JavaScript’s block-scoped declarations.
tinuation marksž in a preprocessing step. In contrast to these
steppers, our stepper directly reduces the given program,
following the presented reduction semantics and resulting 9 Conclusion and Future Work
in a random-access trace data structure. In this approach, we This system description presents the first reduction-based
follow Haskell’s stepper, Hat, which stores the full reduction semantics for a sublanguage of JavaScript and the first al-
trace in a data structure called łaugmented redex trailž. Our gebraic stepper for a language with return statements and
stepper also shares with Hat the strategy of maximizing shar- block-scoped declarations. As partially persistent data struc-
ing for space efficiency, which leads in both systems to data tures, the abstract syntax graphs that represent each step
sharing between steps, coreferences, and cycles and thus enjoy significant sharing among each other, and internally
requires term graph rewriting. The direct syntactic represen- through coreferences and cycles, which enables a memory-
tation of intermediate states simplifies the specification and efficient implementation. Redex highlighting and function
implementation of the tool and facilitates learner interaction skipping contribute to the learner’s experience. Since the
but foregoes opportunities for optimized compilation of the lambda calculus is a subset of the JavaScript sublanguage
learner program. covered by the stepper, it can also be used for for teaching
Broadly speaking, our approach is similar to the way applicative-order-reduction lambda calculus and capture-
rewriting-based operational semantics are implemented in avoiding substitution.
PLT Redex [12], a general framework for designing program- The Source Academy includes a picture language, follow-
ming languages along with their operational semantics. As ing Section 2.2.4 of SICP JS, which serves as a prominent
in PLT Redex, we represent the given programs syntactically example for the substitution model in our course. Future
during reduction, using the data structures that represent versions of the stepper should include an integration of pic-
their components. To maximize data structure sharing, our tures in a similar way in which the Racket IDE integrates
data structures represent recursive functions by cycles and the pictures generated by its Pict library [19]. In addition to
the instances of parameters by coreferences in their syntax function-level skipping, we intend to adopt the fine-grained
graph. control provided by Lambdulus [22] using break points that
As a minimal formal system for computation, the lambda are specified by the learner in the program editor.
calculus [4] is used in teaching the theory of computation and A specification of the reduction system in Coq [5] or light-
programming language semantics (e.g. [12]), often backed up weight mechanization tools (for an overview, see [17]) will
by programming exercises or even complete experimentation allow us to mechanize proofs of correctness of the stepper
environments such as LambdaLab [21] and Lambdulus [22]. or improve testing.
80
A Stepper for a Functional JavaScript Sublanguage SPLASH-E ’21, October 20, 2021, Chicago, IL, USA
References [12] Matthias Felleisen, Robert Bruce Findler, and Matthew Flatt. 2009.
Semantics Engineering with PLT Redex. MIT Press, Cambridge, MA.
[1] Harold Abelson and Gerald Jay Sussman with Julie Sussman. 1996. [13] Github estree repository. 2021. estree. https://github.com/estree/
Structure and Interpretation of Computer Programs (2nd ed.). MIT Press, estree.
Cambridge, MA. [14] Github source-academy organization. 2021. Source Academy. https:
[2] Harold Abelson and Gerald Jay Sussman. 2022. Structure and Interpre- //github.com/source-academy.
tation of Computer Programs, JavaScript edition. MIT Press, Cambridge, [15] Jordan Harband and Kevin Smith (Eds.). 2020. ECMAScript 2020 Lan-
MA. Adapted to JavaScript by Martin Henz and Tobias Wrigstad with guage Specification (11th ed.). Ecma International, Geneva.
Julie Sussman. [16] Martin Henz, Ning-Yuan Lee, and Daryl Tan. 2021. Specification of
[3] Zena M. Ariola and Jan Willem Klop. 1997. Lambda Calculus with Source ğ2. Technical Report. National University of Singapore. https:
Explicit Recursion. Information and Computation 139, 2 (December //source-academy.github.io/source/source_2.pdf
1997), 154ś233. [17] Casey Klein, John Clements, Christos Dimoulas, Carl Eastlund,
[4] Hendrik Pieter Barendregt. 2013. The Lambda Calculus: Its Syntax and Matthias Felleisen, Matthew Flatt, Jay A. McCarthy, Jon Rafkind, and
Semantics (2nd ed.). Elsevier Science, Saint Louis, MO. Sam Tobin-Hochstadt. 2012. Run your research: on the effective-
[5] Yves Bertot and Pierre Castéran. 2013. Interactive theorem proving and ness of lightweight mechanization. In Proceedings of the 39th Annual
program development: Coq’Art: the calculus of inductive constructions. ACM SIGPLAN-SIGACT symposium on Principles of programming lan-
Springer, Berlin/Heidelberg. guages, Michael Hicks (Ed.). POPL 2012, Philadelphia, PA, 285ś296.
[6] Bor-Yuh Evan Chang. 2018. Principles and Practice in Programming https://doi.org/10.1145/2103656.2103691
Languages: A Project-Based Course, University of Colorado, Boulder. [18] Henry Ledgard. 1980. A human engineered variant of BNF. ACM
(September 2018). https://csci3155.cs.colorado.edu/csci3155-notes.pdf. SIGPLAN Notices 15, 10 (1980), 57ś62. https://doi.org/10.1145/947727.
[7] Stephen Chang, John Clements, Eli Barzilay, and Matthias Felleisen. 947732
2011. Stepping Lazy Programs. http://arxiv.org/abs/1108.4706. [19] Racket team. 2021. Pict: Functional Pictures. https://docs.racket-
[8] Olaf Chitil and Yong Luo. 2007. Structure and Properties of Traces for lang.org/pict.
Functional Programs. Electronic Notes in Theoretical Computer Science [20] Racket team. 2021. The Stepper. https://docs.racket-lang.org/stepper.
176, 1 (2007), 39ś63. https://doi.org/10.1016/j.entcs.2006.10.032 [21] Daniel Sainati and Adrian Sampson. 2018. LambdaLab: An Interac-
[9] Zachary Chua, Martin Henz, Peter Jung, Thomas Tan, Yee-Jian Tan, tive 𝜆-Calculus Reducer for Learning. In Proceedings of the 2018 ACM
Xinyi Zhang, and Jingjing Zhao. 2021. Specification of Source ğ2 Step- SIGPLAN Symposium on SPLASH-E, Benjamin Lerner (Ed.). ACM SIG-
per. Source Academy Specifications. National University of Singapore. PLAN, Boston, MA, 10ś19. https://doi.org/10.1145/3310089.3313180
https://docs.sourceacademy.org/source_2_stepper.pdf. [22] Jan Sliacky and Petr Maj. 2019. Lambdulus: Teaching Lambda Calculus
[10] John Clements, Matthew Flatt, and Matthias Felleisen. 2001. Modeling Practically. In Proceedings of the 2019 ACM SIGPLAN Symposium on
an Algebraic Stepper. In Proceedings of the 10th European Symposium SPLASH-E, Elisa Baniassad (Ed.). ACM SIGPLAN, Athens, Greece, 57ś
on Programming Languages and Systems, ESOP’01 (LNCS), David Sands 65. https://doi.org/10.1145/3358711.3361629
(Ed.), Vol. 2028. Springer, Berlin, Heidelberg, New York, 320ś334. https: [23] Source Academy Organization. 2021. Source Academy. https://about.
//doi.org/10.1007/3-540-45309-1_21 sourceacademy.org.
[11] James Driscoll, Neil Sarnak, Daniel Sleator, and Robert Tarjan. 1986. [24] J. Urquiza-Fuentes and J. Á. Velázquez-Iturbide. 2004. A Survey of
Making data structures persistent. In Proceedings of the 18th Annual Program Visualizations for the Functional Paradigm. Proceedings of
ACM Symposium on Theory of Computing, Juris Hartmanis (Ed.). STOC the 3rd Program Visualization Workshop, Research Report CS-RR-407.
1986, Berkeley, CA, 109ś121. https://doi.org/10.1145/12130.12142 Department of Computer Science, University of Warwick, 2ś9.
81