Hazelnut: A Bidirectionally Typed Structure Editor Calculus

18
Hazelnut: A Bidirectionally Typed Structure Editor Calculus Cyrus Omar 1 Ian Voysey 1 Michael Hilton 2 Jonathan Aldrich 1 Matthew A. Hammer 3 1 Carnegie Mellon University 2 Oregon State University 3 University of Colorado Boulder Abstract Structure editors allow programmers to edit the tree structure of a program directly. This can have cognitive benefits, partic- ularly for novice and end-user programmers (as evidenced by the popularity of structure editors like Scratch.) It also sim- plifies matters for tool designers, because they do not need to contend with malformed program text. This paper defines Hazelnut, a structure editor based on a small bidirectionally typed lambda calculus extended with holes and a cursor (à la Huet’s zipper.) Hazelnut goes one step beyond syntactic well-formedness: its edit actions operate over statically meaningful (i.e. well-typed) terms. Naïvely, this prohibition on ill-typed edit states would force the pro- grammer to construct terms in a rigid “outside-in” manner. To avoid this problem, the action semantics automatically places terms assigned a type that is inconsistent with the expected type inside a hole. This safely defers the type consistency check until the term inside the hole is finished. Hazelnut is a foundational type-theoretic account of typed structure editing, rather than an end-user tool itself. To that end, we describe how Hazelnut’s rich metatheory, which we have mechanized in Agda, guides the definition of an exten- sion to the calculus. We also discuss various plausible evalu- ation strategies for terms with holes, and in so doing reveal connections with gradual typing and contextual modal type theory (the Curry-Howard interpretation of contextual modal logic.) Finally, we discuss how Hazelnut’s semantics lends itself to implementation as a functional reactive program. Our reference implementation is written using js_of_ocaml. 1. Introduction Programmers typically construct and manipulate well-typed expressions only indirectly, by editing text that is first parsed according to a textual syntax and then typechecked according to a static semantics. This indirection has practical benefits, to be sure – text editors and other text-based tools benefit from decades of development effort – but it also introduces some fundamental complexity into the programming process. First, it requires that programmers learn the subtleties of the textual syntax (e.g. the precedence of the various forms.) This can be particularly challenging for novices [1]. [Copyright notice will appear here once ’preprint’ option is removed.] The fact that not every sequence of characters corresponds to a meaningful program also complicates matters for tool designers. In particular, program editors must contend with meaningless text on a regular basis. In a dataset analyzed by Yoon and Myers consisting of 1460 hours of edit logs, 44.2% of edit states were syntactically malformed [52]. Some additional percentage of edit states were well-formed but ill-typed (the dataset did not contain enough information to determine the exact percentage.) Some of these meaningless edit states arose because the programmer was in the midst of a sequence of edits that left the edit state transiently meaningless, while others arose because the programmer made a logical mistake. Because the language definition is silent about these edit states, it is difficult to design useful editor services, e.g. syntax highlighting [41], type-aware code completion [28, 31], and refactoring support [25]. Editors must either disable these editor services when they encounter meaningless edit states or develop ad hoc heuristics, which can be misleading. These complications have motivated a long line of research into structure editors, i.e. program editors where every edit state is a program structure. Most structure editors are syntactic structure editors, i.e. the edit state is a syntax tree with holes that stand for branches of the tree that have yet to be constructed, and the edit actions are context-free tree transformations. For example, Scratch is a syntactic structure editor that has achieved success as a tool for teaching children how to program [39]. The Scratch language has a trivial static semantics, but researchers have also designed syntactic structure editors for languages with a non-trivial static semantics. For example, mbeddr is an editor for a C-like language [48], TouchDevelop is an editor for an object-oriented language [45] and Lamdu is an editor for a functional language similar to Haskell [23]. Each of these editors presents an innovative user interface, but the non-trivial type and binding structure of the underlying language complicates its design. The reason is that a syntactic structure editor does not guarantee that every edit state is stat- ically meaningful – only that it is syntactically well-formed. As in textual program editors, these syntactic structure editors must either disable key editor services when they encounter meaningless edit states or deploy ad hoc heuristics. This paper develops a principled solution to this problem. We introduce Hazelnut, a typed structure editor based on a bidirectionally typed lambda calculus extended to assign static meaning to expressions and types with holes, which we call H-expressions and H-types. Hazelnut’s formal action semantics maintains the invariant that every edit state is a statically meaningful (i.e. well-typed) H-expression with a single superimposed cursor. We call H-expressions and H- types with a cursor Z-expressions and Z-types (so prefixed because our encoding follows Huet’s zipper pattern [18].) Draft 1 2021/9/30 arXiv:1607.04180v3 [cs.PL] 13 Oct 2016

Transcript of Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Page 1: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Hazelnut: A Bidirectionally TypedStructure Editor Calculus

Cyrus Omar1 Ian Voysey1 Michael Hilton2 Jonathan Aldrich1 Matthew A. Hammer3

1 Carnegie Mellon University 2 Oregon State University 3 University of Colorado Boulder

AbstractStructure editors allow programmers to edit the tree structureof a program directly. This can have cognitive benefits, partic-ularly for novice and end-user programmers (as evidencedby the popularity of structure editors like Scratch.) It also sim-plifies matters for tool designers, because they do not need tocontend with malformed program text.

This paper defines Hazelnut, a structure editor based ona small bidirectionally typed lambda calculus extended withholes and a cursor (à la Huet’s zipper.) Hazelnut goes one stepbeyond syntactic well-formedness: its edit actions operateover statically meaningful (i.e. well-typed) terms. Naïvely,this prohibition on ill-typed edit states would force the pro-grammer to construct terms in a rigid “outside-in” manner. Toavoid this problem, the action semantics automatically placesterms assigned a type that is inconsistent with the expectedtype inside a hole. This safely defers the type consistencycheck until the term inside the hole is finished.

Hazelnut is a foundational type-theoretic account of typedstructure editing, rather than an end-user tool itself. To thatend, we describe how Hazelnut’s rich metatheory, which wehave mechanized in Agda, guides the definition of an exten-sion to the calculus. We also discuss various plausible evalu-ation strategies for terms with holes, and in so doing revealconnections with gradual typing and contextual modal typetheory (the Curry-Howard interpretation of contextual modallogic.) Finally, we discuss how Hazelnut’s semantics lendsitself to implementation as a functional reactive program. Ourreference implementation is written using js_of_ocaml.

1. IntroductionProgrammers typically construct and manipulate well-typedexpressions only indirectly, by editing text that is first parsedaccording to a textual syntax and then typechecked accordingto a static semantics. This indirection has practical benefits, tobe sure – text editors and other text-based tools benefit fromdecades of development effort – but it also introduces somefundamental complexity into the programming process.

First, it requires that programmers learn the subtleties ofthe textual syntax (e.g. the precedence of the various forms.)This can be particularly challenging for novices [1].

[Copyright notice will appear here once ’preprint’ option is removed.]

The fact that not every sequence of characters correspondsto a meaningful program also complicates matters for tooldesigners. In particular, program editors must contend withmeaningless text on a regular basis. In a dataset analyzed byYoon and Myers consisting of 1460 hours of edit logs, 44.2% ofedit states were syntactically malformed [52]. Some additionalpercentage of edit states were well-formed but ill-typed (thedataset did not contain enough information to determine theexact percentage.) Some of these meaningless edit states arosebecause the programmer was in the midst of a sequence ofedits that left the edit state transiently meaningless, whileothers arose because the programmer made a logical mistake.Because the language definition is silent about these editstates, it is difficult to design useful editor services, e.g. syntaxhighlighting [41], type-aware code completion [28, 31], andrefactoring support [25]. Editors must either disable theseeditor services when they encounter meaningless edit statesor develop ad hoc heuristics, which can be misleading.

These complications have motivated a long line of researchinto structure editors, i.e. program editors where every editstate is a program structure.

Most structure editors are syntactic structure editors, i.e. theedit state is a syntax tree with holes that stand for branches ofthe tree that have yet to be constructed, and the edit actionsare context-free tree transformations. For example, Scratch isa syntactic structure editor that has achieved success as a toolfor teaching children how to program [39].

The Scratch language has a trivial static semantics, butresearchers have also designed syntactic structure editors forlanguages with a non-trivial static semantics. For example,mbeddr is an editor for a C-like language [48], TouchDevelopis an editor for an object-oriented language [45] and Lamduis an editor for a functional language similar to Haskell [23].Each of these editors presents an innovative user interface, butthe non-trivial type and binding structure of the underlyinglanguage complicates its design. The reason is that a syntacticstructure editor does not guarantee that every edit state is stat-ically meaningful – only that it is syntactically well-formed.As in textual program editors, these syntactic structure editorsmust either disable key editor services when they encountermeaningless edit states or deploy ad hoc heuristics.

This paper develops a principled solution to this problem.We introduce Hazelnut, a typed structure editor based on abidirectionally typed lambda calculus extended to assignstatic meaning to expressions and types with holes, whichwe call H-expressions and H-types. Hazelnut’s formal actionsemantics maintains the invariant that every edit state is astatically meaningful (i.e. well-typed) H-expression with asingle superimposed cursor. We call H-expressions and H-types with a cursor Z-expressions and Z-types (so prefixedbecause our encoding follows Huet’s zipper pattern [18].)

Draft 1 2021/9/30

arX

iv:1

607.

0418

0v3

[cs

.PL

] 1

3 O

ct 2

016

Page 2: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Naïvely, enforcing an injunction on ill-typed edit stateswould force programmers to construct programs in a rigid“outside-in” manner. For example, the programmer wouldoften need to construct the outer function application formbefore identifying the intended function. To address thisproblem, Hazelnut leaves newly constructed expressionsinside a hole if the expression’s type is inconsistent with theexpected type. This meaningfully defers the type consistencycheck until the expression inside the hole is finished. In otherwords, holes appear both at the leaves and at the internalnodes of an H-expression that remain under construction.

The remainder of this paper is organized as follows:• We begin in Sec. 2 with two examples of edit sequences to

develop the reader’s intuitions.• We then give a detailed overview of Hazelnut’s semantics

and metatheory, and our mechanization in the Agda proofassistant, in Sec. 3.

• Hazelnut is a foundational calculus, i.e. a calculus thatlanguage and editor designers are expected to extend withhigher level constructs. We extend Hazelnut with simplesum types in Sec. 4 to demonstrate how Hazelnut’s richmetatheory guides such a development.

• In Sec. 5, we briefly describe how Hazelnut’s action seman-tics lends itself to efficient implementation as a functionalreactive program. Our reference implementation is writtenusing js_of_ocaml and the OCaml React library.

• In Sec. 6, we summarize related work. In particular, muchof the technical machinery needed to handle type holescoincides with machinery developed in the study of grad-ual type systems. Similarly, expression holes can be in-terpreted as the closures of contextual modal type theory,which, by its correspondence with contextual modal logic,lays logical foundations beneath our work.

• We conclude in Sec. 7 by summarizing our vision of aprincipled science of structure editor design rooted in typetheory, and suggest a number of future directions.

The supplemental material includes 1) a typeset listing ofHazelnut’s rules in definitional order; 2) the formalization ofHazelnut in Agda; and 3) our reference implementation ofHazelnut, both in source form and pre-compiled to JavaScriptfor use in a web browser.

2. Programming in HazelnutThe reader is encouraged to follow along with the examplesthat we will discuss in this section using the implementation.

For concision, the right column in both Fig. 2.1 and Fig2.2 lists only the rules that describe the edit at the cursor. Thisomits both the rules needed to work down the tree structureof the Z-expression to the cursor and the rules to propagatethe change back up. The missing rules are described in detailin Sec. 3.3.8, and these examples are included in full in theAgda mechanization described in Sec. 3.5.

2.1 Example 1: Constructing the Increment FunctionFigure 1 shows an edit sequence that constructs the incrementfunction, of type num → num, starting from the empty holevia the indicated sequence of actions. We will introduceHazelnut’s formal syntax and define the referenced rules inSec. 3. First, let us build some high-level intuitions.

The edit state in Hazelnut is a Z-expression, e. Every Z-expression has a single H-expression, e, or H-type, τ, underthe cursor, typeset .e/ or .τ/, respectively. For example, onLine 1, the empty expression hole, LM, is under the cursor.

# Z-Expression Next Action Rule1 .LM/ construct lam x (14e)2 λx.LM : .LM/→ LM construct num (13b)3 λx.LM : .num/→ LM move parent (7c)4 λx.LM : .num→ LM/ move child 2 (7b)5 λx.LM : num→ .LM/ construct num (13b)6 λx.LM : num→ .num/ move parent (7d)7 λx.LM : .num→ num/ move parent (9d)8 .λx.LM : num→ num/ move child 1 (9a)9 .λx.LM/ : num→ num move child 1 (9e)

10 λx..LM/ : num→ num construct var x (14c)11 λx..x/ : num→ num construct plus (14l)12 λx.x + .LM/ : num→ num construct lit 1 (14j)13 λx.x + .1/ : num→ num — —

Figure 1. Constructing an increment function in Hazelnut.

now assume incr : num→ num# Z-Expression Next Action Rule

14 .LM/ construct var incr (14c)15 .incr/ construct ap (14h)16 incr(.LM/) construct var incr (14d)17 incr(L.incr/M) construct ap (14h)18 incr(Lincr(.LM/)M) construct lit 3 (14j)19 incr(Lincr(.3/)M) move parent (9j)20 incr(L.incr(3)/M) move parent (9p)21 incr(.Lincr(3)M/) finish (17b)22 incr(.incr(3)/) — —

Figure 2. Appyling the increment function.

Actions act relative to the cursor. The first action that weperform is construct lam x, which instantiates the hole witha lambda abstraction binding the variable x. This results inthe Z-expression on Line 2, consisting of a lambda abstrac-tion with an arrow type ascription. The cursor is placed onthe argument type hole. Type holes are typeset like emptyexpression holes for visual consistency.

The actions on Lines 2-5 complete the type ascription.In particular, the construct num action constructs the numtype at the cursor and composition of the move parent andmove child 2 actions move the cursor to the next hole.

The actions on Lines 6-9 move the cursor to the functionbody. For simplicity, Hazelnut defines only a small collectionof primitive movement actions. In practice, an editor wouldalso define compound movement actions, e.g. an action thatmoves the cursor directly to the next remaining hole, in termsof these primitive movement actions.

Lines 10-12 complete the function body by first construct-ing the variable x, then constructing the plus form, and finallyconstructing the number 1. Notice that we did not need toconstruct the function body in an “outside-in” manner, i.e. weconstructed x before constructing the outer plus form. Luckily,the transient function bodies, x and x + LM, could be checkedagainst type num (as we will detail in Sec. 3.1.)

2.2 Example 2: Applying the Increment FunctionFigure 2 shows an edit sequence that constructs the expressionincr(incr(3)), where incr is assumed bound to the incrementfunction from Figure 1.

We begin on Line 14 by constructing the variable incr. Line15 then constructs the application form with incr in functionposition, leaving the cursor on a hole in the argument position.Notice that we did not need to construct the outer applicationform before identifying the function being applied. Again, the

Draft 2 2021/9/30

Page 3: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

transient edit state happens to be well-typed, so we neededto make no special allowances for this order of actions.

We now need to apply incr again, so we perform the sameaction on Line 16 as we did on Line 14, i.e. construct var incr.In a syntactic structure editor, performing such an actionwould result in the following edit state:

incr(.incr/)This edit state is ill-typed (after cursor erasure): the argumentof incr must be of type num but here it is of type num → num.Hazelnut cannot allow such an edit state to arise.

The programmer could alternatively have performed theconstruct ap action on Line 16. This would result in thefollowing edit state, which is well-typed according to thestatic semantics that we will define in the next section:

incr(LM(.LM/))The problem is that the programmer is not able to identify theintended function before constructing the function applicationform. This stands in contrast to Lines 14-15.

Hazelnut’s action semantics addresses this problem: ratherthan disallowing the construct var incr action on Line 16, itleaves incr inside a hole:

incr(L.incr/M)This defers the type consistency check, exactly as an emptyhole in the same position does. One way to think about non-empty holes is as an internalization of the “squiggly underline”that text or syntactic structure editors display to indicate atype inconsistency. By internalizing this concept, the presenceof a type inconsistency does not leave the entire programformally meaningless.

The expression inside a non-empty hole must itself be well-typed, so the programmer can continue to edit it. Lines 17-18proceed to apply the inner mention of incr to a number literal,3. Finally, Lines 18-19 move the cursor to the non-empty holeand Line 21 performs the finish action. The finish actionremoves the hole if the type of the expression inside the holeis consistent with the expected type, as it now is. This resultsin the final edit state on Line 22, as desired. In practice, theeditor might automatically perform the finish action as soonas it becomes possible, but for simplicity, Hazelnut formallyrequires that it be performed explicitly.

3. Hazelnut, FormallyThe previous section introduced Hazelnut by example. In thissection, we systematically introduce the following structures:• H-types and H-expressions (Sec. 3.1), which are types and

expressions with holes. H-types classify H-expressionsaccording to Hazelnut’s bidirectional static semantics.

• Z-types and Z-expressions (Sec. 3.2), which superimposea cursor onto H-types and H-expressions, respectively(following Huet’s zipper pattern [18].) Every Z-type (resp. Z-expression) corresponds to an H-type (resp. H-expression)by cursor erasure.

• Actions (Sec. 3.3), which act relative to the cursor accord-ing to Hazelnut’s bidirectional action semantics. The ac-tion semantics enjoys a rich metatheory. Of particular note,the sensibility theorem establishes that every edit state iswell-typed after cursor erasure.Our overview below omits certain “uninteresting” details.

The appendix includes the complete collection of rules, indefinitional order. These rules, along with the proofs of all ofthe metatheorems discussed, have been mechanized in Agda[30] (see Sec. 3.5.)

HTyp τ ::= τ → τ | num | LMHExp e ::= e : τ | x | λx.e | e(e) | n | e + e | LM | LeM

Figure 3. Syntax of H-types and H-expressions. Metavariablex ranges over variables and n ranges over numerals.

τ ∼ τ′ τ and τ′ are consistent

LM ∼ τ τ ∼ LM τ ∼ τ

τ1 ∼ τ′1 τ2 ∼ τ′2(τ1 → τ2) ∼ (τ′1 → τ′2)

(1a-d)

τ I→ τ1 → τ2 τ has matched arrow type τ1 → τ2

τ1 → τ2 I→ τ1 → τ2(2a) LM I→ LM→ LM

(2b)

Figure 4. H-type consistency and matched arrow types.

3.1 H-types and H-expressionsFigure 3 defines the syntax of H-types, τ, and H-expressions,e. Most forms correspond directly to those of the simply typedlambda calculus (STLC) extended with a single base type, num,of numbers (cf. [16].) The number expression correspondingto the mathematical number n is drawn n, and for simplicity,we define only a single arithmetic operation, e + e. The forme : τ is an explicit type ascription.

In addition to these standard forms, type holes and emptyexpression holes are both drawn LM and non-empty expressionholes are drawn LeM. We do not need non-empty type holesbecause every H-type is a valid classifier of H-expressions.

Types and expressions that contain no holes are completetypes and complete expressions, respectively. Formally, we canderive τ complete when τ is complete, and e complete when eis complete (see supplement.) We are not concerned here withdefining a dynamic semantics for Hazelnut, but the dynamicsfor complete H-expressions would be entirely standard be-cause complete expressions correspond immediately to thoseof the typed lambda calculus with numbers (in Sec. 6 we dis-cuss the multi-dimensional design space around the questionof evaluating incomplete H-expressions.)

Hazelnut’s static semantics is organized as a bidirectionaltype system [7, 8, 12, 35] around the two mutually definedjudgements in Figure 5. Typing contexts, Γ, map each variablex ∈ dom(Γ) to a hypothesis x : τ in the usual manner. Weidentify the context up to exchange and contraction and adoptthe standard identification convention for formal structuresthat differ only up to alpha-renaming of bound variables. Alljudgements that mention a context obey weakening.

Derivations of the type analysis judgement, Γ ` e ⇐ τ,establish that e can appear where an expression of type τis expected. Derivations of the type synthesis judgement,Γ ` e⇒ τ, infer a type from e, which is necessary in positionswhere an expected type is not available (e.g. at the top level.)Algorithmically, the type is an “input” of the type analysisjudgement, but an “output” of the type synthesis judgement.Making a judgemental distinction between these two notionswill be essential in our action semantics (Sec. 3.3.)

Type synthesis is stronger than type analysis in that if anexpression is able to synthesize a type, it can also be analyzedagainst that type, or any other consistent type, according tothe subsumption rule, Rule (4a).

The H-type consistency judgement, τ ∼ τ′, that appears as apremise in the subsumption rule is a reflexive and symmetric(but not transitive) relation between H-types defined judge-mentally in Figure 4. This relation coincides with equality for

Draft 3 2021/9/30

Page 4: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Γ ` e⇒ τ e synthesizes τ

Γ ` e⇐ τ

Γ ` e : τ ⇒ τ(3a)

Γ, x : τ ` x ⇒ τ(3b)

Γ ` e1 ⇒ τ τ I→ τ2 → τ′ Γ ` e2 ⇐ τ2

Γ ` e1(e2)⇒ τ′(3c)

Γ ` n⇒ num(3d)

Γ ` e1 ⇐ num Γ ` e2 ⇐ numΓ ` e1 + e2 ⇒ num

(3e)

Γ ` LM⇒ LM(3f)

Γ ` e⇒ τ

Γ ` LeM⇒ LM(3g)

Γ ` e⇐ τ e analyzes against τ

Γ ` e⇒ τ′ τ ∼ τ′

Γ ` e⇐ τ(4a)

τ I→ τ1 → τ2 Γ, x : τ1 ` e⇐ τ2

Γ ` λx.e⇐ τ(4b)

Figure 5. Synthesis and analysis.

complete H-types. Two incomplete H-types are consistent ifthey differ only at positions where a hole appears in eithertype. The type hole is therefore consistent with every type.This notion of H-type consistency coincides with the notionof type consistency that Siek and Taha discovered in theirfoundational work on gradual type systems, if we interpretthe type hole as the ? (i.e. unknown) type [42].

Rule (4b) defines analysis for lambda abstractions, λx.e.There is no type synthesis rule that applies to this form, solambda abstractions can appear only in analytic position, i.e.where an expected type is known.1 Rule (4b) is not quite thestandard rule, reproduced below:

Γ, x : τ1 ` e⇐ τ2

Γ ` λx.e⇐ τ1 → τ2The problem is that this standard rule alone leaves us unableto analyze lambda abstractions against the type hole, becausethe type hole is not immediately of the form τ1 → τ2. Thereare two plausible solutions to this problem. One solutionwould be to define a second rule specifically for this case:

Γ, x : LM ` e⇐ LMΓ ` λx.e⇐ LM

Instead, we combine these two possible rules into a single rulethrough the simple auxiliary matched arrow type judgement,τ I→ τ1 → τ2, defined in Figure 4. This judgement leavesarrow types alone and assigns the type hole the matchedarrow type LM→ LM. It is easy to see that the two rules aboveare admissible by appeal to Rule (4b) and the matched arrowtype judgement. Encouragingly, this judgement also arises inthe study of gradual type systems [9, 14, 37].1 It is possible to also define a “half-annotated” synthetic lambdaform, λx:τ.e, but for simplicity, we leave it out [7].

ZTyp τ ::= .τ/ | τ → τ | τ → τZExp e ::= .e/ | e : τ | e : τ | λx.e | e(e) | e(e) | e + e | e + e | LeM

Figure 6. Syntax of Z-types and Z-expressions.

Rule (3a) defines type synthesis of expressions of ascriptionform, e : τ. This allows the user to explicitly state a type forthe ascribed expression to be analyzed against.

Rule (3b) is the standard rule for variables.Rule (3c) is again nearly the standard rule for function

application. It also makes use of the matched function typejudgement to combine what would otherwise need to be tworules for function application – one for when e1 synthesizesan arrow type, and another for when e1 synthesizes the typehole. Indeed, Siek and Taha needed two application rules forthe same fundamental reason [42]. Later work on gradualtyping introduced this notion of type matching to resolve thisredundancy.

Rule (3d) states that numbers synthesize the num type. Rule(3e) states that e1 + e2 behaves like a function over numbers.

The rules described so far are sufficient to type completeH-expressions. The two remaining rules give H-expressionswith holes a well-defined static semantics.

Rule (3f) states that the empty expression hole synthesizesthe type hole.

A non-empty hole contains an H-expression that is “underconstruction”, as described in Sec. 2. According to Rule (3g),this inner expression must synthesize some type, but, like theempty hole, non-empty holes synthesize the type hole.

Given these rules, it is instructive to derive the following(by subsumption):

incr : num→ num ` LincrM⇐ num

3.2 Z-types and Z-expressionsHazelnut’s action semantics operates over Z-types, τ, andZ-expressions, e. Figure 6 defines the syntax of Z-types andZ-expressions. A Z-type (resp. Z-expression) represents an H-type (resp. H-expression) with a single superimposed cursor.

The only base cases in these inductive grammars are .τ/and .e/, which identify the H-type or H-expression that thecursor is on. All of the other forms correspond to the recursiveforms in the syntax of H-types and H-expressions, and containexactly one “hatted” subterm that identifies the subtree wherethe cursor will be found. Any other sub-term is “dotted”, i.e.it is either an H-type or H-expression. Taken together, everyZ-type and Z-expression contains exactly one selected H-typeor H-expression by construction. This can be understood asan instance of Huet’s zipper pattern [18] (which, coincidentally,Huet encountered while implementing a structure editor.)

We write τ� for the H-type constructed by erasing thecursor from τ, which we refer to as the cursor erasure of τ. Thisstraightforward metafunction is defined as follows:

(.τ/)� = τ

(τ → τ)� = τ� → τ

(τ → τ)� = τ → τ�

Similarly, we write e� for the H-expression constructedby erasing the cursor from the Z-expression e, i.e. the cursorerasure of e. The definition of this metafunction is analogous,so we omit it for concision (see supplement.)

3.3 ActionsWe now arrive at the heart of Hazelnut: its bidirectional actionsemantics. Figure 7 defines the syntax of actions, α, some ofwhich involve directions, δ, and shapes, ψ.

Draft 4 2021/9/30

Page 5: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Action α ::= move δ | construct ψ | del | finishDir δ ::= child n | parentShape ψ ::= arrow | num

| asc | var x | lam x | ap | lit n | plus| nehole

Figure 7. Syntax of actions.

Expression actions are governed by two mutually definedjudgements, 1) the synthetic action judgement:

Γ ` e⇒ τα−−→ e′ ⇒ τ′

and 2) the analytic action judgement:

Γ ` e α−−→ e′ ⇐ τ

In some Z-expressions, the cursor is in a type ascription, sowe also need a type action judgement, pronounced “performingα on τ results in τ′”:

τα−−→ τ′

3.3.1 SensibilityBefore giving the rules for these judgements, let us statethe key metatheorem: sensibility. This metatheorem deeplyinforms the design of the rules, given starting in Sec. 3.3.3. Itsproof, which is mechanized in Agda, is by straightforwardinduction, so the reader is encouraged to think about therelevant cases of these proofs as we present the rules.

Theorem 1 (Action Sensibility). Both of the following hold:1. If Γ ` e� ⇒ τ and Γ ` e ⇒ τ

α−−→ e′ ⇒ τ′ thenΓ ` e′� ⇒ τ′.

2. If Γ ` e� ⇐ τ and Γ ` e α−−→ e′ ⇐ τ then Γ ` e′� ⇐ τ.

In other words, if an edit state (i.e. a Z-expression) is stati-cally meaningful, i.e. its cursor erasure is well-typed, thenperforming an action on it leaves the resulting edit state stati-cally meaningful. In particular, the first clause of Theorem 1establishes that when an action is performed on an edit statewhose cursor erasure synthesizes an H-type, the result is anedit state whose cursor erasure also synthesizes some (possi-bly different) H-type. The second clause establishes that whenan action is performed using the analytic action judgementon an edit state whose cursor erasure analyzes against someH-type, the result is a Z-expression whose cursor erasure alsoanalyzes against the same H-type.

3.3.2 Type InconsistencyIn some of the rules below, we will need to supplement ourdefinition of type consistency from Figure 4 with a definitionof type inconsistency, written τ � τ′. One can define thisnotion either directly as the metatheoretic negation of typeconsistency, or as a separate inductively defined judgement.The supplement does the latter. The key rule establishes thatarrow types are inconsistent with num:

num � τ1 → τ2The mechanization proves that this judgemental definition oftype inconsistency is indeed the negation of type consistency.

3.3.3 Action SubsumptionThe action semantics includes a subsumption rule similar tothe subsumption rule, Rule (4a), in the statics:

Γ ` e� ⇒ τ′ Γ ` e⇒ τ′α−−→ e′ ⇒ τ′′ τ ∼ τ′′

Γ ` e α−−→ e′ ⇐ τ(6)

In other words, if the cursor erasure of the edit state synthe-sizes a type, then we defer to the synthetic action judgement,as long as the type of the resulting cursor erasure is consis-

tent with the type provided for analysis. The case involvingRule (6) in the proof of Theorem 1 goes through by induc-tion and static subsumption, i.e. Rule (4a). Algorithmically,subsumption should be the rule of last resort (see Sec. 3.4.)

3.3.4 Relative MovementThe rules below define relative movement within Z-types.They should be self-explanatory:

.τ1 → τ2/move child 1−−−−−−−→ .τ1/→ τ2

(7a)

.τ1 → τ2/move child 2−−−−−−−→ τ1 → .τ2/

(7b)

.τ1/→ τ2move parent−−−−−−−→ .τ1 → τ2/

(7c)

τ1 → .τ2/move parent−−−−−−−→ .τ1 → τ2/

(7d)

Two more rules are needed to recurse into the zipper structure.We define these zipper rules in an action-independent mannerin Sec. 3.3.8.

The rules for relative movement within Z-expressions aresimilarly straightforward. Movement is type-independent, sowe defer to an auxiliary expression movement judgement inboth the analytic and synthetic case:

e move δ−−−−→ e′

Γ ` e⇒ τmove δ−−−−→ e′ ⇒ τ

(8a)

e move δ−−−−→ e′

Γ ` e move δ−−−−→ e′ ⇐ τ(8b)

The expression movement judgement is defined as follows.

Ascription

.e : τ/move child 1−−−−−−−→ .e/ : τ

(9a)

.e : τ/move child 2−−−−−−−→ e : .τ/

(9b)

.e/ : τmove parent−−−−−−−→ .e : τ/

(9c)

e : .τ/move parent−−−−−−−→ .e : τ/

(9d)

Lambda

.λx.e/ move child 1−−−−−−−→ λx..e/(9e)

λx..e/move parent−−−−−−−→ .λx.e/

(9f)

Application

.e1(e2)/move child 1−−−−−−−→ .e1/(e2)

(9g)

.e1(e2)/move child 2−−−−−−−→ e1(.e2/)

(9h)

.e1/(e2)move parent−−−−−−−→ .e1(e2)/

(9i)

e1(.e2/)move parent−−−−−−−→ .e1(e2)/

(9j)

Plus

.e1 + e2/move child 1−−−−−−−→ .e1/+ e2

(9k)

.e1 + e2/move child 2−−−−−−−→ e1 + .e2/

(9l)

.e1/+ e2move parent−−−−−−−→ .e1 + e2/

(9m)

e1 + .e2/move parent−−−−−−−→ .e1 + e2/

(9n)

Draft 5 2021/9/30

Page 6: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Non-Empty Hole

.LeM/ move child 1−−−−−−−→ L.e/M(9o)

L.e/Mmove parent−−−−−−−→ .LeM/

(9p)

Again, additional rules are needed to recurse into the zipperstructure, but we will define these zipper rules in an action-independent manner in Sec. 3.3.8.

The rules above are numerous and fairly uninteresting.That makes them quite hazardous – we might make a mistakeabsent-mindedly. One check against this is to prove thatestablishes that movement actions do not change the cursorerasure, as in Theorem 2.

Theorem 2 (Movement Erasure Invariance).1. If τ

move δ−−−−→ τ′ then τ� = τ′�.

2. If Γ ` e� ⇒ τ and Γ ` e ⇒ τmove δ−−−−→ e′ ⇒ τ′ then e� = e′�

and τ = τ′.3. If Γ ` e� ⇐ τ and Γ ` e move δ−−−−→ e′ ⇐ τ then e� = e′�.

Theorem 2 is useful also in that the relevant cases of Theorem1 are straightforward by its application.

Another useful check is to establish reachability, i.e. that itis possible, through a sequence of movement actions, to movethe cursor from any position to any other position within awell-typed H-expression.

This requires developing machinery for reasoning aboutsequences of actions. There are two possibilities: we can eitheradd a sequencing action, α; α, directly to the syntax of actions,or we can define a syntax for lists of actions, α, together withiterated action judgements. To keep the core of the actionsemantics small, we take the latter approach in Figure 8.

A simple auxiliary judgement, α movements, defined in thesupplement, establishes that α consists only of actions of theform move δ.

With these definitions, we can state reachability as follows:

Theorem 3 (Reachability).1. If τ� = τ′� then there exists some α such that α movements

and τα−−→∗ τ′.

2. If Γ ` e� ⇒ τ and e� = e′� then there exists some α such thatα movements and Γ ` e⇒ τ

α−−→∗ e′ ⇒ τ.3. If Γ ` e� ⇐ τ and e� = e′� then there exists some α such that

α movements and Γ ` e α−−→∗ e′ ⇐ τ.

The simplest way to prove Theorem 3 is to break it intotwo lemmas. Lemma 4 establishes that you can always movethe cursor to the outermost position in an expression. Thisserves as a check on our move parent rules.

Lemma 4 (Reach Up).1. If τ� = τ then there exists some α such that α movements and

τα−−→∗ .τ/.

2. If Γ ` e ⇒ τ and e� = e then there exists some α such thatα movements and Γ ` e⇒ τ

α−−→∗ .e/⇒ τ.3. If Γ ` e ⇐ τ and e� = e then there exists some α such that

α movements and Γ ` e α−−→∗ .e/⇐ τ.

Lemma 5 establishes that you can always move the cursorfrom the outermost position to any other position. This servesas a check on our move child n rules.

Lemma 5 (Reach Down).1. If τ� = τ then there exists some α such that α movements and

.τ/α−−→∗ τ.

ActionList α ::= · | α; α

τα−−→∗ τ′

τ·−−→∗ τ

(10a) τα−−→ τ′ τ′

α−−→∗ τ′′

τα;α−−→∗ τ′′

(10b)

Γ ` e⇒ τα−−→∗ e′ ⇒ τ′

Γ ` e⇒ τ·−−→∗ e⇒ τ

Γ ` e⇒ τα−−→ e′ ⇒ τ′

Γ ` e′ ⇒ τ′α−−→∗ e′′ ⇒ τ′′

Γ ` e⇒ τα;α−−→∗ e′′ ⇒ τ′′

(11a-b)

Γ ` e α−−→ e′ ⇐ τ

Γ ` e ·−−→∗ e⇐ τ

Γ ` e α−−→ e′ ⇐ τ

Γ ` e′ α−−→∗ e′′ ⇐ τ

Γ ` e α;α−−→∗ e′′ ⇐ τ(12a-b)

Figure 8. Iterated Action Judgements

2. If Γ ` e ⇒ τ and e� = e then there exists some α such thatα movements nand Γ ` .e/⇒ τ

α−−→∗ e⇒ τ.3. If Γ ` e ⇐ τ and e� = e then there exists some α such that

α movements and Γ ` .e/ α−−→∗ e⇐ τ.

Theorem 3 follows by straightforward composition of thesetwo lemmas.

3.3.5 ConstructionThe construction actions, construct ψ, are used to constructterms of a shape indicated by ψ at the cursor.

Types The construct arrow action constructs an arrow type.The H-type under the cursor becomes the argument type, andthe cursor is placed on an empty return type hole:

.τ/construct arrow−−−−−−−−−−→ τ → .LM/

(13a)

The construct num action replaces an empty type holeunder the cursor with the num type:

.LM/ construct num−−−−−−−−→ .num/(13b)

Ascription The construct asc action operates differently de-pending on whether the H-expression under the cursor syn-thesizes a type or is being analyzed against a type. In the firstcase, the synthesized type appears in the ascription:

Γ ` .e/⇒ τconstruct asc−−−−−−−−→ e : .τ/⇒ τ

(14a)

In the second case, the type provided for analysis appears inthe ascription:

Γ ` .e/ construct asc−−−−−−−−→ e : .τ/⇐ τ(14b)

Variables The construct var x action places the variable xinto an empty hole. If that hole is being asked to synthesize atype, then the result synthesizes the hypothesized type:

Γ, x : τ ` .LM/⇒ LM construct var x−−−−−−−−−→ .x/⇒ τ(14c)

If the hole is being analyzed against a type that is consistentwith the hypothesized type, then the action semantics goesthrough the action subsumption rule described in Sec. 3.3.3. Ifthe hole is being analyzed against a type that is inconsistent

Draft 6 2021/9/30

Page 7: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

with the hypothesized type, x is placed inside a hole:τ � τ′

Γ, x : τ′ ` .LM/ construct var x−−−−−−−−−→ L.x/M⇐ τ(14d)

The rule above featured on Line 15 of Figure 2.

Lambdas The construct lam x action places a lambda ab-straction binding x into an empty hole. If the empty hole isbeing asked to synthesize a type, then the result of the actionis a lambda ascribed the type LM→ LM, with the cursor on theargument type hole:

Γ ` .LM/⇒ LM construct lam x−−−−−−−−−→ λx.LM : .LM/→ LM⇒ LM→ LM(14e)

The type ascription is necessary because lambda expressionsdo not synthesize a type. If the empty hole is being analyzedagainst a type with matched arrow type, then no ascription isnecessary:

τ I→ τ1 → τ2

Γ ` .LM/ construct lam x−−−−−−−−−→ λx..LM/⇐ τ(14f)

Finally, if the empty hole is being analyzed against a typethat has no matched arrow type, expressed in the premiseas inconsistency with LM → LM, then a lambda ascribed thetype LM→ LM is inserted inside a hole, which defers the typeinconsistency as previously discussed:

τ � LM→ LM

Γ ` .LM/ construct lam x−−−−−−−−−→ Lλx.LM : .LM/→ LMM⇐ τ(14g)

Application The construct ap action applies the expressionunder the cursor. The following rule handles the case wherethe synthesized type has matched function type:

τ I→ τ1 → τ2

Γ ` .e/⇒ τconstruct ap−−−−−−−−→ e(.LM/)⇒ τ2

(14h)

If the expression under the cursor synthesizes a type thatis inconsistent with an arrow type, then we must place thatexpression inside a hole to maintain Theorem 3.1:

τ � LM→ LM

Γ ` .e/⇒ τconstruct ap−−−−−−−−→ LeM(.LM/)⇒ LM

(14i)

Numbers The construct lit n action replaces an empty holewith the number expression n. If the empty hole is being askedto synthesize a type, then the rule is straightforward:

Γ ` .LM/⇒ LM construct lit n−−−−−−−−−−→ .n/⇒ num(14j)

If the empty hole is being analyzed against a type thatis inconsistent with num, then we must place the numberexpression inside the hole:

τ � num

Γ ` .LM/ construct lit n−−−−−−−−−−→ L.n/M⇐ τ(14k)

The construct plus action constructs a plus expressionwith the expression under the cursor as its first argument. Ifthat expression synthesizes a type consistent with num, thenthe rule is straightforward:

τ ∼ num

Γ ` .e/⇒ τconstruct plus−−−−−−−−−→ e + .LM/⇒ num

(14l)

Otherwise, we must place that expression inside a hole:τ � num

Γ ` .e/⇒ τconstruct plus−−−−−−−−−→ LeM+ .LM/⇒ num

(14m)

Non-Empty Holes The final shape is nehole. This explicitlyplaces the expression under the cursor inside a hole:

Γ ` .e/⇒ τconstruct nehole−−−−−−−−−−→ L.e/M⇒ LM

(14n)

The nehole shape is grayed out in Figure 7 because we donot expect the programmer to perform it explicitly – otheractions automatically insert holes when a type inconsistencywould arise. Its inclusion is mainly to make it easier to stateanother “checksum” theorem: constructabilityConstructability To check that we have defined “enough”construct actions, we need to establish that we can start froman empty hole and arrive at any well-typed expression with,for simplicity, the cursor on the outside (Lemma 5 allows usto then move the cursor anywhere else.) As with reachability,we rely on the iterated action judgements defined in Figure 8.

Theorem 6 (Constructability).1. For every τ there exists α such that .LM/ α−−→∗ .τ/.2. If Γ ` e⇒ τ then there exists α such that:

Γ ` .LM/⇒ LM α−−→∗ .e/⇒ τ

3. If Γ ` e⇐ τ then there exists α such that:

Γ ` .LM/ α−−→∗ .e/⇐ τ

3.3.6 DeletionThe del action inserts an empty hole at the cursor, deletingwhat was there before.

The type action rule for del is self-explanatory:

.τ/del−−→ .LM/

(15)

Deletion within a Z-expression is similarly straightforward:

Γ ` .e/⇒ τdel−−→ .LM/⇒ LM

(16a)

Γ ` .e/ del−−→ .LM/⇐ τ(16b)

Unlike the relative movement and construction actions, thereis no “checksum” theorem for deletion. The rules do notinspect the structure of the expression in the cursor, so theyboth match our intuition and will be correct in any extensionof the language without modification.

3.3.7 FinishingThe final action we need to consider is finish, which finishesthe non-empty hole under the cursor.

If the non-empty hole appears in synthetic position, thenit can always be finished:

Γ ` e⇒ τ′

Γ ` .LeM/⇒ LM finish−−−−→ .e/⇒ τ′(17a)

If the non-empty hole appears in analytic position, then itcan only be finished if the type synthesized for the envelopedexpression is consistent with the type that the hole is beinganalyzed against. This amounts to analyzing the envelopedexpression against the provided type (by subsumption):

Γ ` e⇐ τ

Γ ` .LeM/ finish−−−−→ .e/⇐ τ(17b)

Like deletion, there is no need for a “checksum” theorem forthe finishing.

3.3.8 Zipper CasesThe rules defined so far handle the base cases, i.e. the caseswhere the action has “reached” the expression under the cur-sor. We also need to define the recursive cases, which propa-

Draft 7 2021/9/30

Page 8: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

gate the action into the subtree where the cursor appears, asencoded by the zipper structure. For types, the zipper rulesare straightforward:

τα−−→ τ′

τ → τα−−→ τ′ → τ

(18a)

τα−−→ τ′

τ → τα−−→ τ → τ′

(18b)

For expressions, the zipper rules essentially follow the struc-ture of the corresponding rules in the statics.

In particular, when the cursor is in the body of a lambdaexpression, the zipper case mirrors Rule (4b):

τ I→ τ1 → τ2 Γ, x : τ1 ` e α−−→ e′ ⇐ τ2

Γ ` λx.e α−−→ λx.e′ ⇐ τ(19a)

When the cursor is in the expression position of an ascrip-tion, we use the analytic judgement, mirroring Rule (3a):

Γ ` e α−−→ e′ ⇐ τ

Γ ` e : τ ⇒ τα−−→ e′ : τ ⇒ τ

(19b)

When the cursor is in the type position of an ascription,we must re-check the ascribed expression because the cursorerasure might have changed (in practice, one would optimizethis check to only occur if the cursor erasure did change):

τα−−→ τ′ Γ ` e⇐ τ′�

Γ ` e : τ ⇒ τ�α−−→ e : τ′ ⇒ τ′�

(19c)

When the cursor is in the function position of an applica-tion, the rule mirrors Rule (3c):

Γ ` e� ⇒ τ2 Γ ` e⇒ τ2α−−→ e′ ⇒ τ3

τ3 I→ τ4 → τ5 Γ ` e⇐ τ4

Γ ` e(e)⇒ τ1α−−→ e′(e)⇒ τ5

(19d)

The situation is similar when the cursor is in argumentposition:

Γ ` e⇒ τ2 τ2 I→ τ3 → τ4 Γ ` e α−−→ e′ ⇐ τ3

Γ ` e(e)⇒ τ1α−−→ e(e′)⇒ τ4

(19e)

The rules for the addition operator mirror Rule (3e):

Γ ` e α−−→ e′ ⇐ num

Γ ` e + e⇒ num α−−→ e′ + e⇒ num(19f)

Γ ` e α−−→ e′ ⇐ num

Γ ` e + e⇒ num α−−→ e + e′ ⇒ num(19g)

Finally, if the cursor is inside a non-empty hole, the rele-vant zipper rule mirrors Rule (3f):

Γ ` e� ⇒ τ Γ ` e⇒ τα−−→ e′ ⇒ τ′

Γ ` LeM⇒ LM α−−→ Le′M⇒ LM(19h)

Theorem 1 directly checks the correctness of these rules.Moreover, the zipper rules arise ubiquitously in derivationsof edit steps, so the proofs of the other “check” theorems, e.g.Reachability and Constructability, serve as a check that noneof these rules have been missed.

3.4 DeterminismA last useful property to consider is action determinism, i.e. thatperforming an action produces a unique result. The followingtheorem establishes determinism for type actions:

Theorem 7 (Type Action Determinism). If τα−−→ τ′ and

τα−−→ τ′′ then τ′ = τ′′.

The corresponding theorem for expression actions wouldbe stated as follows:1. If Γ ` e� ⇒ τ and Γ ` e ⇒ τ

α−−→ e′ ⇒ τ′ andΓ ` e⇒ τ

α−−→ e′′ ⇒ τ′′ then e′ = e′′ and τ′ = τ′′.2. If Γ ` e� ⇐ τ and Γ ` e α−−→ e′ ⇐ τ and Γ ` e α−−→ e′′ ⇐ τ

then e′ = e′′.This is not a theorem of the system as described so far.

The reason is somewhat subtle: two construction actions,construct asc and construct lam x, behave differently inthe analytic case than they do in the synthetic case. Theproblem is that both rules can “fire” when considering a Z-expression in analytic position due to action subsumption.This is a technically valid but “morally” invalid use of actionsubsumption: subsumption is included in the system to beused as a rule of last resort, i.e. it should only be applied whenno other analytic action rule can fire.

There are several possible ways to address this problem.One approach would be to modify the judgement forms tointernalize this notion of “rule of last resort”. This approachis related to focusing from proof theory [43]. However, thisapproach would substantially complicate our presentation ofthe system.

The approach that we take leaves the system unchanged.Instead, we define predicates over derivations of the expressionaction judgements that exclude those derivations that applysubsumption prematurely, i.e. when another rule could havebeen applied. We call such derivations subsumption-minimalderivations. We can establish determinism for subsumption-minimal derivations.Theorem 8 (Expression Action Determinism).1. If Γ ` e� ⇒ τ and D1 : Γ ` e ⇒ τ

α−−→ e′ ⇒ τ′ andD2 : Γ ` e ⇒ τ

α−−→ e′′ ⇒ τ′′ and submin⇒(D1) andsubmin⇒(D2) then e′ = e′′ and τ′ = τ′′.

2. If Γ ` e� ⇐ τ and D1 : Γ ` e α−−→ e′ ⇐ τ and D2 : Γ `e α−−→ e′′ ⇐ τ and submin⇐(D1) and submin⇐(D2) thene′ = e′′.The mechanization, discussed next, defines the predicates

submin⇒(D) and submin⇐(D). In addition, it defines a map-ping from any derivation into a corresponding subsumption-minimal derivation. Implementations of Hazelnut need onlyimplement this subsumption-minimal fragment.

3.5 MechanizationIn order to formally establish that our design meets our statedobjectives, we have mechanized the semantics and metatheoryof Hazelnut as described above (and more concisely in theappendix) using the Agda proof assistant [30] (also see theAgda Wiki, hosted at http://wiki.portal.chalmers.se/agda/.) This development is available in the supplementalmaterial. The mechanization also includes the extension tothe language described in Sec. 4.

The documentation includes a more detailed discussion ofthe technical representation decisions that we made. The mainidea is standard: we encode each judgement as a dependenttype. The rules defining the judgements become the construc-tors of this type, and derivations are terms of these type. Thisis a rich setting that allows proofs to take advantage of pat-tern matching on the shape of derivations, closely matchingstandard on-paper proofs. No proof automation was used,because the proof structure itself is likely to be interesting toresearchers who plan to build upon our work.

We adopt Barendregt’s convention for bound variables [46].Hazelnut’s semantics does not need substitution, so we do notneed to adopt more sophisticated encodings (e.g. [22, 36].)

Draft 8 2021/9/30

Page 9: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

4. Extending HazelnutIn this section, we will conservatively extend Hazelnut withbinary sum types to demonstrate how the rules and the richmetatheory developed in the previous section serve to guideand constrain this and other such efforts.

Syntax. The first step is to extend the syntax of H-types andH-expressions with the familiar forms [16]:

HTyp τ ::= · · · | τ + τHExp e ::= · · · | inji(e) | case(e, x.e, y.e)

Recall that binary sum types introduce a new type-levelconnective, τ1 + τ2. The introductory forms are the injections,inji(e); here, we consider only binary sums, so i ∈ {1, 2}. Theelimination form is case analysis, case(e, x.e1, y.e2).

Next, we must correspondingly extend the syntax of Z-types and Z-expressions, following Huet’s zipper pattern [18]:

ZTyp τ ::= · · · | τ + τ | τ + τZExp e ::= · · · | inji(e) | case(e, x.e, y.e)

| case(e, x.e, y.e) | case(e, x.e, y.e)

Notice that for each H-type or H-expression form of arityn, there are n corresponding Z-type or Z-expression forms,each of which has a single “hatted” subterm. The remainingsubterms are “dotted”. We must also extend the definition ofcursor erasure, e.g. for types:

(τ + τ)� = τ� + τ

(τ + τ)� = τ + τ�

The rules for Z-expressions are analogous:inji(e)

� = inji(e�)

case(e, x.e1, y.e2)� = case(e�, x.e1, y.e2)

case(e, x.e1, y.e2)� = case(e, x.e�1 , y.e2)

case(e, x.e1, y.e2)� = case(e, x.e1, y.e�2)

Finally, we must extend the syntax of shapes:

Shape ψ ::= · · · | sum | inj i | case x y

Notice that for each H-type or H-expression form, there isa corresponding shape. The injection form had a formal pa-rameter, i, so the corresponding shape takes a correspondingargument (like lit n.) The case form included two variablebinders, so the corresponding shape takes two variable argu-ments (like lam x.)

Statics. We can now move on to the static semantics.First, we must extend the type consistency relation as

shown in Figure 9, following the example of covariant typeconsistency rule for arrow types in Figure 4. Similarly, weneed a notion of a matched sum type spiritually analogous tothe notion of a matched arrow type defined in Figure 4.

The type analysis rules shown in Figure 9 are essentiallystandard, differing only in that instead of immediately requir-ing that a type be of the form τ1 + τ2, we use the matched sumtype judgement. We combine the two injection rules for con-cision and define only a type analysis rule for the case formfor simplicity (see [9] for additional machinery that would benecessary for a synthetic rule.)

Action Semantics. Figures 10 and 11 extend Hazelnut’s actionsemantics to support bidirectionally typed structure editingwith sums.

Rule (24a), the construction rule for sum types, and Rules(24b)-(24c), the zipper rules for sum types, follow the corre-sponding rules for arrow types. Were we to have missed any

τ1 ∼ τ2 τ1 ∼ τ′1 τ2 ∼ τ′2τ1 + τ2 ∼ τ′1 + τ′2

(21)

τ I+ τ1 + τ2 τ has matched sum type τ1 + τ2

τ1 + τ2 I+ τ1 + τ2(22a) LM I+ LM+ LM

(22b)

Γ ` e⇐ τ

τ+ I+ τ1 + τ2 Γ ` e⇐ τi

Γ ` inji(e)⇐ τ+(i ∈ {1, 2}) (23a)

Γ ` e⇒ τ+ τ+ I+ τ1 + τ2Γ, x : τ1 ` e1 ⇐ τ Γ, y : τ2 ` e2 ⇐ τ

Γ ` case(e, x.e1, y.e2)⇐ τ(23b)

Figure 9. The statics of sums.

τα−−→ τ′

.τ/construct sum−−−−−−−−→ τ + .LM/

(24a)

τα−−→ τ′

τ + τα−−→ τ′ + τ

(24b)τ

α−−→ τ′

τ + τα−−→ τ + τ′

(24c)

Γ ` e α−−→ e′ ⇐ τ

τ+ I+ τ1 + τ2

Γ ` .LM/construct inj i−−−−−−−−−→ inji(.LM/)⇐ τ+

(25a)

τ � LM+ LM

Γ ` .LM/construct inj i−−−−−−−−−→ Linji(LM) : .LM/+ LMM⇐ τ

(25b)

Γ ` .LM/construct case x y−−−−−−−−−−−→ case(.LM/, x.LM, y.LM)⇐ τ

(25c)

τ+ I+ τ1 + τ2 Γ ` e α−−→ e′ ⇐ τi

Γ ` inji(e)α−−→ inji(e

′)⇐ τ+(i ∈ {1, 2}) (25d)

Γ ` e� ⇒ τ0 Γ ` e⇒ τ0α−−→ e′ ⇒ τ+ τ+ I+ τ1 + τ2

Γ, x : τ1 ` e1 ⇐ τ Γ, y : τ2 ` e2 ⇐ τ

Γ ` case(e, x.e1, y.e2)α−−→ case(e′, x.e1, y.e2)⇐ τ

(25e)

Γ ` e⇒ τ+ τ+ I+ τ1 + τ2 Γ, x : τ1 ` e1α−−→ e′1 ⇐ τ

Γ ` case(e, x.e1, y.e2)α−−→ case(e, x.e′1, y.e2)⇐ τ

(25f)

Γ ` e⇒ τ+ τ+ I+ τ1 + τ2 Γ, y : τ2 ` e2α−−→ e′2 ⇐ τ

Γ ` case(e, x.e1, y.e2)α−−→ case(e, x.e1, y.e′2)⇐ τ

(25g)

Γ ` e⇒ τα−−→ e′ ⇒ τ′

Γ ` .LM/⇒ τconstruct inj i−−−−−−−−−→ inji(LM) : .LM/+ LM⇒ LM+ LM

(26a)τ I+ τ1 + τ2

Γ ` .e/⇒ τconstruct case x y−−−−−−−−−−−→ case(e, x..LM/, y.LM) : LM⇒ LM

(26b)τ � LM+ LM

Γ ` .e/⇒ τconstruct case x y−−−−−−−−−−−→ case(L.e/M, x.LM, y.LM) : LM⇒ LM

(26c)

Figure 10. The construction & zipper action rules for sums.

Draft 9 2021/9/30

Page 10: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

τmove δ−−−−→ τ′ (Additional type movement rules:)

.τ1 + τ2/move child 1−−−−−−−→ .τ1/+ τ2

.τ1 + τ2/move child 2−−−−−−−→ τ1 + .τ2/

.τ1/+ τ2move parent−−−−−−−→ .τ1 + τ2/

τ1 + .τ2/move parent−−−−−−−→ .τ1 + τ2/

e move δ−−−−→ e′ (Additional expression movement rules:)

.inji(e)/move child 1−−−−−−−→ inji(.e/)

inji(.e/)move parent−−−−−−−→ .inji(e)/

.case(e, x.e1, y.e2)/move child 1−−−−−−−→ case(.e/, x.e1, y.e2)

.case(e, x.e1, y.e2)/move child 2−−−−−−−→ case(e, x..e1/, y.e2)

.case(e, x.e1, y.e2)/move child 3−−−−−−−→ case(e, x.e1, y..e2/)

case(.e/, x.e1, y.e2)move parent−−−−−−−→ .case(e, x.e1, y.e2)/

case(e, x..e1/, y.e2)move parent−−−−−−−→ .case(e, x.e1, y.e2)/

case(e, x.e1, y..e2/)move parent−−−−−−−→ .case(e, x.e1, y.e2)/

Figure 11. Movement actions for sums.

of these, the first clause of Theorem 6, i.e. Constructability,would not be conserved.

Rule (25a) constructs an injection when the type providedfor analysis has a matched sum type. This is analogous to Rule(14f) for lambdas. Rule (25b) constructs an injection when thetype provided for analysis is not consistent with sum types.This is analogous to Rule (14g) for lambdas. Rule (25c) isa straightforward rule for constructing case expressions inempty holes. Rules (25d)-(25g) are the zipper cases, whichfollow the structure of the statics. Finally, we also define asingle new synthetic action rule, Rule (26a), which allows forthe construction of an injection in synthetic position, withautomatic insertion of an ascription. This is analogous to Rule(14e). If we had defined any of these rules incorrectly, theSensibility Theorem (Theorem 1) would not be conserved.Had we forgotten the analytic rules, the ConstructabilityTheorem (Theorem 6) would not be conserved.

Figure 11 gives the relevant movement axioms. For conci-sion and clarity, we write these axioms in tabular form. Hadwe made a mistake in any of these rules, the Movement Era-sure Invariance theorem (Theorem 2) would not be conserved.Had we forgotten any of these rules, the Reachability Theorem(Theorem 3) would not be conserved.

5. Implementation5.1 Implementation ConceptsCentral to any implementation of Hazelnut is a stream ofedit states whose cursor erasures synthesize types under anempty context according to the synthetic action judgement,∅ ` e ⇒ τ

α−−→ e′ ⇒ τ′. The middle row of Figure12 diagrams this stream of edit states. By Theorem 1, theeditor does not need to typecheck each edit state (though insome of the rules, e.g. Rule (19c) which handles the situationwhere the type in a type ascription changes, portions of theprogram do need to be typechecked.) For example, the readeris encouraged to re-examine the examples in Figure 1 and 2 –the cursor erasure of each edit state synthesizes a type.

The programmer examines a view generated from eachedit state and produces actions in some implementation-

α1

actions

edit states

act

view

α2 …

v1 v2

views

v3

α1 α2ê2⇒τ2ê1⇒τ1 ê3⇒τ3

Programmer

Figure 12. Implementation Concepts

defined manner (e.g. using a keyboard, mouse, touchscreen,voice interface, or neural implant.) Each new action causes anew abstract edit state to arise according to an implementationof the action semantics. This then causes a new view to arise.This is a simple event-based functional reactive programmingmodel [51].

If an action is not well-defined according to Hazelnut’saction semantics, the implementation must reject it. In fact, theimplementation is encouraged to present an “action palette”that either hides or visibly disables actions that are not well-defined (see below.)

5.2 HZWe have developed a simple reference implementation, HZ, ofHazelnut extended with sum types as described in Sec. 4. Inorder to reach a wide audience, we decided to implementHZ in the web browser. To take advantage of a matureimplementation of the FRP model, we chose to implementHZ using OCaml2, the js_of_ocaml compiler and associatedlibraries [2]3 and the OCaml React library4.

The core semantics have been implemented in a functionalstyle that follows the presentation in this paper closely.

The view computation renders the model (i.e. a Z-expression paired with a type) as nested HTML div elementsmatching the tree structure of the corresponding Z-expression.This tree is stylized separately using CSS. The action palette isa collection of buttons and text boxes which are disabled whenthe corresponding action cannot be performed. We determinethis by simply attempting to perform the action internallyand handling the exception that is raised when an action isundefined. Each action form also has a corresponding key-board shortcut. This design is not, of course, meant to scoremarks for usability or performance. Instead, as a simple refer-ence implementation, it allows the reader to interact with thesystem presented in this paper, to aid in their understanding.Additionally, we expect its source code to be of use to otherswho are interested in layering a more fluid user interface atopthe core semantics, or extensions thereof.

6. Related Work and Discussion6.1 Structure EditorsSyntactic structure editors have a long history – the CornellProgram Synthesizer [44] was first introduced in 1981. Noviceprogrammers have been a common target for structure editors.For example, GNOME [15] was developed to teach program-ming to undergraduates. Alice [10] is a 3-D programming lan-guage with an integrated structure editor for teaching novice

2 https://ocaml.org/3 http://ocsigen.org/js_of_ocaml/4 http://erratique.ch/software/react

Draft 10 2021/9/30

Page 11: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

CS undergraduate students. Scratch [39] is a structure editortargeted at children ages 8 to 16. TouchDevelop [45] incor-porates a structure editor for programming on touch-baseddevices, and is used to teach high school students. An imple-mentation of Hazelnut might be useful in teaching studentsabout the typed lambda calculus, though that has not beenour explicit aim with this work.

Not all structure editors are for educational purposes. Forexample, mbeddr [48] is a structure editor for a C-based pro-gramming language (nominally, for programming embeddedsystems.) Lamdu [23], like Hazelnut, is a structure editor fora statically typed functional language. It is designed for useby professional programmers.

The examples given so far either define a language witha trivial static semantics, or do not attempt to maintain well-typedness as an edit invariant. This can pose problems, forreasons discussed in the Introduction. One apparent excep-tion is Unison [6], a structure editor for a typed functionallanguage similar to Haskell. Like Hazelnut, it seems to de-fine some notion of well-typedness for expressions with holes(though there is no technical documentation on virtually anyaspect of its design.) Unlike Hazelnut, it does not have anotion analogous to Hazelnut’s notion of a non-empty hole.As such, programmers must construct programs in a rigidoutside-in manner, as discussed in Sec. 2. Another systemwith the same problem is Polymorphic Blocks, a block-baseduser interface where the structure of block connectors encodesa type [21].

We fundamentally differ from these projects in our designphilosophy: we consider it essential to start by building typetheoretic foundations, which are independent of nearly alldecisions about the user interface. In contrast, these editorshave developed innovative user interfaces (e.g. see the discus-sion in [49]) but lack a principled foundational calculus. Inthis respect, we follow the philosophical approach taken bylanguages that are rooted in the type theoretic tradition andhave gone to great effort to develop a clear metatheory, likeStandard ML [17, 26]. In the future, we hope that these lines ofresearch will merge to produce a human-usable typed struc-ture editor with sound formal foundations. Our contribution,then, is in defining and analyzing the theoretical propertiesof a small foundational calculus that could be extended toachieve this vision.

Some structure editor generators do operate on formal orsemi-formal definitions of an underlying language. For exam-ple, the Synthesizer Generator [38] allows the user to define anattribute grammar-based language implementation that thencan be used to generate a structured editor. CENTAUR [3]produces a language specific environment from a user definedformal specification of a language. Barista is a programmatictoolkit for building structure editors [19]. mbeddr is built ontop of the commercial JetBrains MPS framework for construct-ing structure editors [47, 50]. These systems do not give asemantics to the edit states of the structure editor itself, ormaintain non-trivial edit invariants, as Hazelnut does.

Related to structure editors are value editors, which oper-ate directly on simple values (but not generally expressionsor functions) of a programming language. For example, Erosis a typed value editor based in Haskell [13].

6.2 Gradual Type SystemsA significant contribution of this paper is the discovery of aclear technical relationship between typed structure editingand gradual typing. In particular, the machinery necessary togive a reasonable semantics to type holes – i.e. type consis-

tency and type matching – coincides with that developed inthe study of gradual type systems for functional languages.The pioneering work of Siek and Taha [42] introduced typeconsistency. Subsequent work developed the concept of typematching [14, 37]. In retrospect, this relationship is perhapsunsurprising: gradual typing is, notionally, also motivated bythe idea of iterated development of a program where everyintermediate state is well-defined in some sense.

Recent work has discovered a systematic procedure forgenerating a “gradual version” of a standard type system[9]. This system, called the Gradualizer, operates on a logicprogram that specifies a simple type assignment system withsome additional annotations to generate a corresponding spec-ification of a gradual type system. The authors leave supportfor working with bidirectional type systems as future work.This suggests the possibility of an analogous “Editorializer”that generates a specification of a typed structure editor froma simple language definition. Our exposition in Sec. 4 certainlysuggests that many of the necessary definitions follow seem-ingly mechanically from the definition of the static semantics,and the relationship with gradual typing suggests that manyof the technical details of this transformation may alreadyexist in the Gradualizer. One possibility we have exploredinformally is to use Agda’s reflection features to implementsuch a system.

An aspect of gradual typing that we did not touch ondirectly here is its concern with assigning a dynamics toprograms where type information is not known, by insertingdynamic type casts. This would correspond to assigning adynamics to Hazelnut expressions with type holes such thata run-time error occurs when a type hole is found to beunfillable through evaluation.

6.3 Type Holes as Unification VariablesAnother possible interpretation of type holes is as explicitunification variables. In other words, we might define thedynamics of Hazelnut such that a program cannot be run ifsome automated type inference procedure cannot staticallyfill in any type holes that arise. For a simple calculus, e.g.the STLC upon which Hazelnut is based, type inferenceis known to be decidable [11]. In more complex settings,e.g. in a dependently typed language, a partial decisionprocedure may still be useful in this regard, both at edit-timeand (just prior to) run-time. Indeed, text editor modes forproof assistants, e.g. for Agda, attempt to do exactly this forindicated “type holes” (and do not always succeed.)

6.4 ExceptionsExpression holes, too, could dynamically be interpreted inseveral ways. One straightforward approach would be todynamically raise an exception if evaluation encounters an ex-pression hole. Indeed, placing raise Unimplemented or simi-lar in portions of an expression that are under construction isa common practice across programming languages.

6.5 Type-Directed Program SynthesisSome text editor modes, e.g. those for proof assistants likeAgda [30] and Idris [4], support a more explicit hole-basedprogramming model where indicated expression holes aretreated as sites where the system can be asked to automaticallygenerate an expression of an appropriate type. These systemsare not statically well-defined themselves (though see below.)

The Graphite system used an informal model of typedholes in Java to allow library providers to associate interactivecode generation interfaces with types [31].

Draft 11 2021/9/30

Page 12: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

More generally, the topic of type-directed program syn-thesis an active area of research, e.g. [33]. By maintainingstatic well-definedness throughout the editing process, Hazel-nut provides researchers interested in editor-integrated type-directed program synthesis with a formal foundation uponwhich to build.

6.6 Contextual Modal Type TheoryExpression holes can also be understood by invoking thenotion of a metavariable as found in contextual modal typetheory (CMTT) [29]. In particular, expression holes have typesand are surrounded by contexts, just as metavariables inCMTT are associated with types and contexts. This helps toclarify the logical meaning of a typing derivation in Hazelnut– it conveys well-typedness relative to an (implicit) modalcontext that extracts each expression hole’s type and context.The modal context must be emptied – i.e. the expression holesmust be instantiated with expressions of the proper type inthe proper context – before the expression can be consideredcomplete. This corresponds to the notion of modal necessityin contextual modal logic.

Making the modal context explicit in our semantics isnot technically useful given our stated purpose – interactiveprogram editing is not merely hole filling in Hazelnut (i.e. thecursor need not be on a hole). Moreover, the hole’s type andcontext become apparent as our action semantics traversesthe zipper structure on each step. Some interactive proofassistants, however, support a tactic model based directly onhole filling, so the connection to CMTT and similar systems ismore useful. For example, Beluga [34] is based on dependentCMTT and aspects of Idris’ editor support [4] are based onMcBride’s OLEG [24] and Lee and Friedman have explored alambda calculus with contexts for a similar purpose [20].

One interesting avenue of future work is to elaborateexpression holes to CMTT’s closures, i.e. CMTT terms of theform clo(u; id(Γ)) where u is a unique metavariable associatedwith each hole and id(Γ) is the explicit identity substitution.This would allow us to evaluate expressions with holes suchthat the closure “accumulates” substitutions explicitly. Whenevaluation gets “stuck” (as it can, for CMTT does not definea dynamics equipped with a notion of progress under anon-empty modal context), it would then be possible for theprogrammer to choose holes from amongst the visible holes(which may have been duplicated) to edit in their originalcontext. Once finished, the CMTT hole instantiation operation,together with a metatheorem that establishes that reductioncommutes with instantiation, would enable an “edit andresume” feature with a clear formal basis. This notion ofreduction commuting with instantiation has also been studiedin other calculi [40]. Being able to edit a running program alsohas connections to less formal work on “live programming”interfaces [5, 23].

6.7 Evaluation Strategies: A High-Dimensional SpaceTo summarize, we have discussed three different evaluationstrategies in the presence of type holes:

1. ...as preventing evaluation (the standard approach.)2. ...as unknown types, in the gradual sense.3. ...as unification variables.

In addition, we have discussed four different evaluationstrategies in the presence of expression holes:

1. ...as preventing evaluation (the standard approach.)2. ...as causing exceptions.3. ...as sites for automatic program synthesis.4. ...as the closures of CMTT.

Every combination of these choices could well be consid-ered in the design of a full-scale programming system in thespirit of Hazelnut. Indeed, the user might be given severaloptions from among these combinations, depending on theirusage scenario. Many of these warrant further inquiry.

7. ConclusionThis paper presented Hazelnut, a type theoretic structureeditor calculus. Our aim was to take a principled approach toits design by formally defining its static semantics as wellas its action semantics and developing a rich metatheory.Moreover, we have mechanized substantial portions of themetatheory, including the crucial Sensibility theorem thatestablishes that every edit state is statically meaningful.

In addition to simplifying the job of an editor designer,typed structure editors also promise to increase the speedof development by eliminating redundant syntax and sup-porting higher-level primitive actions. However, we did notdiscuss such “edit costs” here, because they depend on par-ticular implementation details, e.g. whether a keyboard or amouse is in use. Indeed, we consider it a virtue of this workthat such implementation details do not enter into our design.

7.1 Future Work7.1.1 Richer LanguagesHazelnut is, obviously, a very limited language at its core.So the most obvious avenue for future work is to increasethe expressive power of this language by extension. Ourplan is to simultaneously maintain a mechanization andimplementation (following, for example, Standard ML) as weproceed, ultimately producing the first large-scale, formallyverified bidirectionally typed structure editor.

It is interesting to note that the demarcation between thelanguage and the editor is fuzzy (indeed, non-existent) inHazelnut. There may well be interesting opportunities inlanguage design when the language is being codesignedwith a typed structure editor. It may be that certain languagefeatures are unnecessary given a sufficiently advanced type-aware structure editor (e.g. SML’s open?), while other featuresmay only be practical with editor support. We intend to useHazelnut and derivative systems thereof as a platform forrigorously exploring such questions.

7.1.2 Editor ServicesThere are various aspects of the editor model that we havenot yet formalized. For example, our action model does notconsider how actions are actually entered using, for example,key combinations or chords. In practice, we would want alsoto rank available actions in some reasonable manner (perhapsbased on usage data gathered from other users or coderepositories.) Designing a rigorous typed probability modelover actions and H-expressions is one avenue of research thatwe have started to explore, with intriguing initial results.

7.1.3 Programmable ActionsOur language of actions is intentionally primitive. However,even now it acts much like a simple imperative commandlanguage. This suggests future expansion to, for example, atrue tactic language. Alternatively, it may be more useful todevelop the notion of an action macro, whereby functionalprograms could themselves be lifted to the level of actionsto compute non-trivial compound actions. Such compoundactions would give a uniform description of transformationsranging from the simple – like “move the cursor to thenext hole to the right” – to quite complex whole program

Draft 12 2021/9/30

Page 13: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

refactoring, while remaining subject to the core Hazelnutmetatheory.

7.1.4 ViewsAnother research direction is in exploring how types canbe used to control the presentation of expressions in theeditor. For example, following an approach developed in atextual setting of developing type-specific languages (TSLs),it should be possible to have the type that an expression isbeing analyzed against define alternative display forms andinteraction modes [32].

It should also be possible to develop a semantics of seman-tic comments, i.e. comments that mention semantic structures.These would be subject to the same operations, e.g. renam-ing, as other structures, helping to address the problem ofcomments becoming inconsistent with code.

7.1.5 Collaborative ProgrammingFinally, we did not consider any aspects of collaborative pro-gramming, such as a packaging system, a differencing algo-rithm for use in a source control system, support for multiplesimultaneous focii for different users, and so on. These are allinteresting avenues for future work.

7.1.6 Empirical EvaluationAlthough we make few empirical claims in this paper, itis ultimately an empirical question as to whether structureeditors, and typed structure editors, are practical. We hopeto conduct user studies once a richer semantics has beendeveloped.

7.1.7 More TheoryConnections with gradual type systems and CMTT, discussedin the previous section, seem likely to continue to be revealing.

The notion of having one of many possible locations withina term under a cursor has a very strong intuitive connectionto the proof theoretic notion of focusing [43]. Building closerconnections with proof theory (and category theory) is likelyto be a fruitful avenue of further inquiry.

In any case, these are but steps toward more graphicalprogram-description systems, for we will not forever stayconfined to mere strings of symbols.— Marvin Minsky, Turing Award lecture [27]

AcknowledgmentsThanks to Ed Morehouse for counsel on mechanization andBarendrecht’s Convention, and to Vincent Zeng for pro bonoartistic services.

Draft 13 2021/9/30

Page 14: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

References[1] A. Altadmri and N. C. Brown. 37 Million Compilations: Inves-

tigating Novice Programming Mistakes in Large-Scale StudentData. In ACM Technical Symposium on Computer Science Education(SIGCSE), 2015.

[2] V. Balat. Ocsigen: Typing Web Interaction with Objective Caml.In ACM Workshop on ML, 2006.

[3] P. Borras, D. Clément, T. Despeyroux, J. Incerpi, G. Kahn, B. Lang,and V. Pascual. CENTAUR: The System. In Proceedings of theACM SIGSOFT/SIGPLAN Software Engineering Symposium onPractical Software Development Environments, pages 14–24, 1988.

[4] E. Brady. Idris, A General-Purpose Dependently Typed Pro-gramming Language: Design and Implementation. Journal ofFunctional Programming, 23(05):552–593, 2013.

[5] S. Burckhardt, M. Fähndrich, P. de Halleux, S. McDirmid,M. Moskal, N. Tillmann, and J. Kato. It’s alive! continuous feed-back in UI programming. In PLDI, 2013.

[6] P. Chiusano. Unison. http://www.unisonweb.org/. Accessed:2016-04-25.

[7] A. Chlipala, L. Petersen, and R. Harper. Strict bidirectional typechecking. In ACM SIGPLAN International Workshop on Types inLanguages Design and Implementation (TLDI), 2005.

[8] D. R. Christiansen. Bidirectional Typing Rules: A Tutorial. http://davidchristiansen.dk/tutorials/bidirectional.pdf,2013.

[9] M. Cimini and J. G. Siek. The gradualizer: a methodology andalgorithm for generating gradual type systems. In POPL, 2016.

[10] M. Conway, S. Audia, T. Burnette, D. Cosgrove, and K. Chris-tiansen. Alice: Lessons Learned from Building a 3D System forNovices. In SIGCHI Conference on Human Factors in ComputingSystems (CHI), 2000.

[11] L. Damas and R. Milner. Principal type-schemes for functionalprograms. In POPL, 1982.

[12] R. Davies and F. Pfenning. Intersection types and computationaleffects. In ICFP, 2000.

[13] C. Elliott. Tangible Functional Programming. In ICFP, 2007.[14] R. Garcia and M. Cimini. Principal Type Schemes for Gradual

Programs. In POPL, 2015.[15] D. B. Garlan and P. L. Miller. GNOME: An introductory pro-

gramming environment based on a family of structure editors.In Proceedings of the First ACM SIGSOFT/SIGPLAN Software Engi-neering Symposium on Practical Software Development Environments,1984.

[16] R. Harper. Practical Foundations for Programming Languages. 2ndedition, 2016. URL https://www.cs.cmu.edu/~rwh/plbook/2nded.pdf.

[17] R. Harper and C. Stone. A Type-Theoretic Interpretation ofStandard ML. In Proof, Language and Interaction: Essays in Honourof Robin Milner. MIT Press, 2000.

[18] G. Huet. The Zipper. Journal of Functional Programming, 7(5),Sept. 1997. Functional Pearl.

[19] A. J. Ko and B. A. Myers. Barista: An Implementation Frameworkfor Enabling New Tools, Interaction Techniques and Viewsin Code Editors. In SIGCHI Conference on Human Factors inComputing Systems (CHI), 2006.

[20] S. Lee and D. P. Friedman. Enriching the Lambda Calculus withContexts: Toward a Theory of Incremental Program Construction.In ICFP, 1996.

[21] S. Lerner, S. R. Foster, and W. G. Griswold. Polymorphic blocks:Formalism-inspired UI for structured connectors. In ACMConference on Human Factors in Computing Systems (CHI), 2015.

[22] D. R. Licata and R. Harper. A Universe of Binding and Compu-tation. In ICFP, 2009.

[23] E. Lotem and Y. Chuchem. Project Lamdu. http://www.lamdu.org/. Accessed: 2016-04-08.

[24] C. McBride. Dependently typed functional programs and their proofs.PhD thesis, University of Edinburgh. College of Science andEngineering. School of Informatics., 2000.

[25] T. Mens and T. Tourwé. A survey of software refactoring. IEEETransactions on Software Engineering, 30(2):126–139, 2004.

[26] R. Milner, M. Tofte, R. Harper, and D. MacQueen. The Definitionof Standard ML (Revised). The MIT Press, 1997.

[27] M. Minsky. Form and content in computer science (1970 ACMTuring Lecture). J. ACM, 17(2):197–215, 1970.

[28] M. Mooty, A. Faulring, J. Stylos, and B. A. Myers. Calcite: Com-pleting code completion for constructors using crowds. In IEEESymposium on Visual Languages and Human-Centric Computing(VL/HCC), 2010.

[29] A. Nanevski, F. Pfenning, and B. Pientka. Contextual modal typetheory. ACM Trans. Comput. Log., 9(3), 2008.

[30] U. Norell. Towards a practical programming language based ondependent type theory. PhD thesis, Department of ComputerScience and Engineering, Chalmers University of Technology,SE-412 96 Göteborg, Sweden, September 2007.

[31] C. Omar, Y. Yoon, T. D. LaToza, and B. A. Myers. Active codecompletion. In International Conference on Software Engineering(ICSE), 2012.

[32] C. Omar, D. Kurilova, L. Nistor, B. Chung, A. Potanin, andJ. Aldrich. Safely composable type-specific languages. In ECOOP,2014.

[33] P. Osera and S. Zdancewic. Type-and-example-directed programsynthesis. In PLDI, 2015.

[34] B. Pientka. Beluga: Programming with dependent types, contex-tual data, and contexts. In International Symposium on Functionaland Logic Programming (FLOPS), 2010.

[35] B. C. Pierce and D. N. Turner. Local type inference. ACM Trans.Program. Lang. Syst., 22(1):1–44, Jan. 2000.

[36] N. Pouillard. Nameless, painless. In ICFP, 2011.

[37] A. Rastogi, A. Chaudhuri, and B. Hosmer. The ins and outs ofgradual type inference. In POPL, 2012.

[38] T. Reps and T. Teitelbaum. The synthesizer generator. SIGSOFTSoftw. Eng. Notes, 9(3):42–48, Apr. 1984. ISSN 0163-5948.

[39] M. Resnick, J. Maloney, A. Monroy-Hernández, N. Rusk, E. East-mond, K. Brennan, A. Millner, E. Rosenbaum, J. Silver, B. Silver-man, and Y. Kafai. Scratch: Programming for All. Commun. ACM,52(11):60–67, Nov. 2009.

[40] D. Sands. Computing with contexts: A simple approach. Electr.Notes Theor. Comput. Sci., 10:134–149, 1997.

[41] A. Sarkar. The impact of syntax colouring on program compre-hension. In Annual Conference of the Psychology of ProgrammingInterest Group (PPIG), 2015.

[42] J. G. Siek and W. Taha. Gradual typing for functional languages.In Scheme and Functional Programming Workshop, 2006. URLhttp://scheme2006.cs.uchicago.edu/13-siek.pdf.

[43] R. J. Simmons and F. Pfenning. Weak Focusing for OrderedLinear Logic. Technical Report CMU-CS-10-147, Carnegie MellonUniversity, 2011. Revision of April 2011.

[44] T. Teitelbaum and T. Reps. The Cornell Program Synthesizer: ASyntax-directed Programming Environment. Commun. ACM, 24(9):563–573, 1981.

[45] N. Tillmann, M. Moskal, J. de Halleux, and M. Fahndrich.TouchDevelop: Programming Cloud-connected Mobile Devicesvia Touchscreen. In SIGPLAN Symposium on New Ideas, NewParadigms, and Reflections on Programming and Software, 2011.

[46] C. Urban, S. Berghofer, and M. Norrish. Barendregt’s variableconvention in rule inductions. In Conference on AutomatedDeduction (CADE), 2007.

[47] M. Voelter. Language and IDE Modularization and Compositionwith MPS. In International Summer School on Generative and

Draft 14 2021/9/30

Page 15: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Transformational Techniques in Software Engineering, pages 383–430.Springer, 2011.

[48] M. Voelter, D. Ratiu, B. Schaetz, and B. Kolb. Mbeddr: An Exten-sible C-based Programming Language and IDE for EmbeddedSystems. In SPLASH, 2012.

[49] M. Voelter, J. Siegmund, T. Berger, and B. Kolb. Towards User-Friendly Projectional Editors. In International Conference onSoftware Language Engineering (SLE), 2014.

[50] M. Voelter, J. Warmer, and B. Kolb. Projecting a Modular Future.IEEE Software, 32(5):46–52, 2015.

[51] Z. Wan and P. Hudak. Functional Reactive Programming fromFirst Principles. In PLDI, 2000.

[52] Y. S. Yoon and B. A. Myers. A longitudinal study of programmers’backtracking. In IEEE Symposium on Visual Languages and Human-Centric Computing (VL/HCC), 2014.

Draft 15 2021/9/30

Page 16: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

A. HazelnutThe full collection of rules defining the semantics of Hazelnutare reproduced here in their definitional order for reference.

A.1 H-Types and H-ExpressionsA.1.1 Type Compatibility and Incompatibility

τ ∼ τ′

LM ∼ τ(27a)

τ ∼ LM(27b)

τ ∼ τ(27c)

τ1 ∼ τ′1 τ2 ∼ τ′2(τ1 → τ2) ∼ (τ′1 → τ′2)

(27d)

τ � τ′

τ1 → τ2 � num(28a)

num � τ1 → τ2(28b)

τ1 � τ′1τ1 → τ2 � τ′1 → τ′2

(28c)

τ2 � τ′2τ1 → τ2 � τ′1 → τ′2

(28d)

A.1.2 Function Type Matching

τ I→ τ1 → τ2 τ has matched arrow type τ1 → τ2

τ1 → τ2 I→ τ1 → τ2(29a)

LM I→ LM→ LM(29b)

A.1.3 Synthesis and AnalysisThe judgements Γ ` e⇒ τ and Γ ` e⇐ τ are defined mutu-ally inductively by Rules (30) and Rules (31), respectively.

Γ ` e⇒ τ e synthesizes τ

Γ ` e⇐ τ

Γ ` e : τ ⇒ τ(30a)

Γ, x : τ ` x ⇒ τ(30b)

Γ ` e1 ⇒ τ τ I→ τ2 → τ′ Γ ` e2 ⇐ τ2

Γ ` e1(e2)⇒ τ′(30c)

Γ ` n⇒ num(30d)

Γ ` e1 ⇐ num Γ ` e2 ⇐ numΓ ` e1 + e2 ⇒ num

(30e)

Γ ` LM⇒ LM(30f)

Γ ` e⇒ τ

Γ ` LeM⇒ LM(30g)

Γ ` e⇐ τ e analyzes against τ

Γ ` e⇒ τ′ τ ∼ τ′

Γ ` e⇐ τ(31a)

τ I→ τ1 → τ2 Γ, x : τ1 ` e⇐ τ2

Γ ` λx.e⇐ τ(31b)

A.1.4 Complete H-Types and H-ExpressionsBy convention, we use the metavariable τ rather than τfor complete H-types, and e rather than e for complete H-expressions.

τ complete

τ1 complete τ2 complete

τ1 → τ2 complete(32a)

num complete(32b)

e complete

e complete τ complete

e : τ complete(33a)

x complete(33b)

e complete

λx.e complete(33c)

e1 complete e2 complete

e1(e2) complete(33d)

n complete(33e)

e1 complete e2 complete

e1 + e2 complete(33f)

A.2 Z-Types and Z-ExpressionsA.2.1 Type Focus Erasure

τ� = τ is a metafunction defined as follows:(.τ/)� = τ (34a)

(τ → τ)� = τ� → τ (34b)(τ → τ)� = τ → τ� (34c)

A.2.2 Expression Focus Erasure

e� = e is a metafunction defined as follows:(.e/)� = e (35a)(e : τ)� = e� : τ (35b)(e : τ)� = e : τ� (35c)(λx.e)� = λx.e� (35d)(e(e))� = e�(e) (35e)(e(e))� = e(e�) (35f)(e + e)� = e� + e (35g)(e + e)� = e + e� (35h)

LeM� = Le�M (35i)

A.3 Action ModelA.3.1 Type Actions

τα−−→ τ′

Type Movement

.τ1 → τ2/move child 1−−−−−−−→ .τ1/→ τ2

(36a)

.τ1 → τ2/move child 2−−−−−−−→ τ1 → .τ2/

(36b)

.τ1/→ τ2move parent−−−−−−−→ .τ1 → τ2/

(36c)

τ1 → .τ2/move parent−−−−−−−→ .τ1 → τ2/

(36d)

Type Deletion

.τ/del−−→ .LM/

(36e)

Type Construction

.τ/construct arrow−−−−−−−−−−→ τ → .LM/

(36f)

Draft 16 2021/9/30

Page 17: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

.LM/ construct num−−−−−−−−→ .num/(36g)

Zipper Casesτ

α−−→ τ′

τ → τα−−→ τ′ → τ

(36h)

τα−−→ τ′

τ → τα−−→ τ → τ′

(36i)

A.3.2 Expression Movement Actions

e move δ−−−−→ e′

Ascription

.e : τ/move child 1−−−−−−−→ .e/ : τ

(37a)

.e : τ/move child 2−−−−−−−→ e : .τ/

(37b)

.e/ : τmove parent−−−−−−−→ .e : τ/

(37c)

e : .τ/move parent−−−−−−−→ .e : τ/

(37d)

Lambda

.λx.e/ move child 1−−−−−−−→ λx..e/(37e)

λx..e/move parent−−−−−−−→ .λx.e/

(37f)

Application

.e1(e2)/move child 1−−−−−−−→ .e1/(e2)

(37g)

.e1(e2)/move child 2−−−−−−−→ e1(.e2/)

(37h)

.e1/(e2)move parent−−−−−−−→ .e1(e2)/

(37i)

e1(.e2/)move parent−−−−−−−→ .e1(e2)/

(37j)

Plus

.e1 + e2/move child 1−−−−−−−→ .e1/+ e2

(37k)

.e1 + e2/move child 2−−−−−−−→ e1 + .e2/

(37l)

.e1/+ e2move parent−−−−−−−→ .e1 + e2/

(37m)

e1 + .e2/move parent−−−−−−−→ .e1 + e2/

(37n)

Non-Empty Hole

.LeM/ move child 1−−−−−−−→ L.e/M(37o)

L.e/Mmove parent−−−−−−−→ .LeM/

(37p)

A.3.3 Synthetic and Analytic Expression ActionsThe synthetic and analytic expression action performancejudgements are defined mutually inductively by Rules (38)and Rules (39), respectively.

Γ ` e⇒ τα−−→ e′ ⇒ τ′

Movemente move δ−−−−→ e′

Γ ` e⇒ τmove δ−−−−→ e′ ⇒ τ

(38a)

Deletion

Γ ` .e/⇒ τdel−−→ .LM/⇒ LM

(38b)

Construction

Γ ` .e/⇒ τconstruct asc−−−−−−−−→ e : .τ/⇒ τ

(38c)

Γ, x : τ ` .LM/⇒ LM construct var x−−−−−−−−−→ .x/⇒ τ(38d)

Γ ` .LM/⇒ LM construct lam x−−−−−−−−−→ λx.LM : .LM/→ LM⇒ LM→ LM(38e)

τ I→ τ1 → τ2

Γ ` .e/⇒ τconstruct ap−−−−−−−−→ e(.LM/)⇒ τ2

(38f)

τ � LM→ LM

Γ ` .e/⇒ τconstruct ap−−−−−−−−→ LeM(.LM/)⇒ LM

(38g)

Γ ` .LM/⇒ LM construct lit n−−−−−−−−−−→ .n/⇒ num(38h)

τ ∼ num

Γ ` .e/⇒ τconstruct plus−−−−−−−−−→ e + .LM/⇒ num

(38i)

τ � num

Γ ` .e/⇒ τconstruct plus−−−−−−−−−→ LeM+ .LM/⇒ num

(38j)

Γ ` .e/⇒ τconstruct nehole−−−−−−−−−−→ L.e/M⇒ LM

(38k)

FinishingΓ ` e⇒ τ′

Γ ` .LeM/⇒ LM finish−−−−→ .e/⇒ τ′(38l)

Zipper Cases

Γ ` e α−−→ e′ ⇐ τ

Γ ` e : τ ⇒ τα−−→ e′ : τ ⇒ τ

(38m)

τα−−→ τ′ Γ ` e⇐ τ′�

Γ ` e : τ ⇒ τ�α−−→ e : τ′ ⇒ τ′�

(38n)

Γ ` e� ⇒ τ2 Γ ` e⇒ τ2α−−→ e′ ⇒ τ3

τ3 I→ τ4 → τ5 Γ ` e⇐ τ4

Γ ` e(e)⇒ τ1α−−→ e′(e)⇒ τ5

(38o)

Γ ` e⇒ τ2 τ2 I→ τ3 → τ4 Γ ` e α−−→ e′ ⇐ τ3

Γ ` e(e)⇒ τ1α−−→ e(e′)⇒ τ4

(38p)

Γ ` e α−−→ e′ ⇐ num

Γ ` e + e⇒ num α−−→ e′ + e⇒ num(38q)

Γ ` e α−−→ e′ ⇐ num

Γ ` e + e⇒ num α−−→ e + e′ ⇒ num(38r)

Γ ` e� ⇒ τ Γ ` e⇒ τα−−→ e′ ⇒ τ′

Γ ` LeM⇒ LM α−−→ Le′M⇒ LM(38s)

Γ ` e α−−→ e′ ⇐ τ

Subsumption

Γ ` e� ⇒ τ′ Γ ` e⇒ τ′α−−→ e′ ⇒ τ′′ τ ∼ τ′′

Γ ` e α−−→ e′ ⇐ τ(39a)

Draft 17 2021/9/30

Page 18: Hazelnut: A Bidirectionally Typed Structure Editor Calculus

Movemente move δ−−−−→ e′

Γ ` e move δ−−−−→ e′ ⇐ τ(39b)

Deletion

Γ ` .e/ del−−→ .LM/⇐ τ(39c)

Construction

Γ ` .e/ construct asc−−−−−−−−→ e : .τ/⇐ τ(39d)

τ � τ′

Γ, x : τ′ ` .LM/ construct var x−−−−−−−−−→ L.x/M⇐ τ(39e)

τ I→ τ1 → τ2

Γ ` .LM/ construct lam x−−−−−−−−−→ λx..LM/⇐ τ(39f)

τ � LM→ LM

Γ ` .LM/ construct lam x−−−−−−−−−→ Lλx.LM : .LM/→ LMM⇐ τ(39g)

τ � num

Γ ` .LM/ construct lit n−−−−−−−−−−→ L.n/M⇐ τ(39h)

FinishingΓ ` e⇐ τ

Γ ` .LeM/ finish−−−−→ .e/⇐ τ(39i)

Zipper Cases

τ I→ τ1 → τ2 Γ, x : τ1 ` e α−−→ e′ ⇐ τ2

Γ ` λx.e α−−→ λx.e′ ⇐ τ(39j)

A.3.4 Iterated Action Judgements

ActionList α ::= · | α; α

τα−−→∗ τ′

τ·−−→∗ τ

(40a)

τα−−→ τ′ τ′

α−−→∗ τ′′

τα;α−−→∗ τ′′

(40b)

Γ ` e⇒ τα−−→∗ e′ ⇒ τ′

Γ ` e⇒ τ·−−→∗ e⇒ τ

(41a)

Γ ` e⇒ τα−−→ e′ ⇒ τ′ Γ ` e′ ⇒ τ′

α−−→∗ e′′ ⇒ τ′′

Γ ` e⇒ τα;α−−→∗ e′′ ⇒ τ′′

(41b)

Γ ` e α−−→∗ e′ ⇐ τ

Γ ` e ·−−→∗ e⇐ τ(42a)

Γ ` e α−−→ e′ ⇐ τ Γ ` e′ α−−→∗ e′′ ⇐ τ

Γ ` e α;α−−→∗ e′′ ⇐ τ(42b)

α movements

· movements(43a)

α movements

move δ; α movements(43b)

Draft 18 2021/9/30