DOKK Library

A Stepper for a Functional JavaScript Sublanguage

Authors Jingjing Zhao Martin Henz Peter Jung Thomas Tan Xinyi Zhang Yee-Jian Tan Zachary Chua

License CC-BY-SA-4.0

Plaintext
     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