Abstract
Bidirectional live programming is a programming paradigm that enhances the developer experience by allowing direct modifications to the output, with changes instantly reflected back to the source program. Existing operation-based approaches allow modifying the constant values and expressions of programs, while they lack control over modifications originating from multiple updatable locations. To control the updatability of the source program, our approach introduces non-updatable expressions, which guide the fusion of updates to the intended locations. The correctness properties, GetPut and WeakPutGet, ensure output consistency. We have implemented a prototype, FuseSVG, and tested it with 14 nontrivial SVG-drawing benchmarks, demonstrating the effectiveness of our approach.
1. Introduction
Programming can be mentally taxing as it requires us to envision the execution of code during the editing process. Live programming [1,2,3], however, alleviates this strain by continuously re-executing the program during the editing phase, thereby establishing a seamless feedback loop between the programmer and the program. Bidirectional live programming [4] further enhances the developer’s experience by providing a distinctive feature, known as direct manipulations [5], which allows for direct modifications to the output. These changes are instantaneously reflected back to the source program, guaranteeing that the updated program can reproduce the manipulated output.
Sketch-n-Sketch [4,6,7,8] is a bidirectional live programming system that allows direct manipulation on the output, eventually causing the system to substitute old constants in the program with new ones.
The code in Figure 1 creates a rectangle with a width that is twice its height (expressed as mult 2 h in the code). If a programmer attempts to widen the rectangle horizontally while maintaining the height, they would be unsuccessful. This is because any change to the rectangle’s width in the SVG results in an update to the rectangle’s height in the program, due to the default update policy that modifies the right operand of the expression mult 2 h. Sketch-n-Sketch provides a freeze operation that uses ! to mark a constant, indicating that it cannot be modified. If the constant value of height 100 is marked with !, the rectangle becomes un-stretchable. Furthermore, if the programmer wants to change the expression from mult 2 h to mult 2 h + 50 by directly manipulating the output, thereby establishing a new correlation between width and height, this is unachievable as the system only permits updates to constants. To summarize, Sketch-n-Sketch allows for the modification of constants, yet it constrains the adjustment of expressions, such as the correlation between width and height.
Figure 1.
An output-directed manipulation example in Sketch-n-Sketch.
FuseDM [9] fuses direct manipulations into source program by propagating them to constants whenever possible. In order to satisfy the correctness that the execution of the updated program yields the same manipulated output, sometimes direct manipulations will be embedded into a “proper position”.
Figure 2 depicts the same rectangle. If the programmer stretches the rectangle horizontally, the expression h*2, which represents the width of the rectangle, is updated to h*2+dh, where dh represents the displacement value, and the height h remains unchanged. Although FuseDM conveys the intention of updating the expression, it fails to express the intention of maintaining the relationship between width and height by adjusting the width. This is particularly important when drawing in SVG, as numerous relationships between SVG elements must be preserved.
Figure 2.
A manipulation-directed example in FuseDM.
In general, we propose an approach that sits between Sketch-n-Sketch and FuseDM, which is capable of preserving the relationships between SVG elements during manipulation, while also facilitating program updates. These updates encompass more than just changes to constant values; they also involve modifications to expressions. We developed FuseSVG with the following features: (1) We introduce a const keyword that can be used to designate an expression as non-changeable. This guides the fusion of manipulation, ensuring that marked expressions are not updated during propagation to prevent unintended changes. (2) The correctness is guaranteed by the GETPUT and WeakPUTGET properties. Since the relation is stored in the program, fusion followed by execution of updated program ensures that elements in SVG are consistent according to the relation. Moreover, the WeakPUTGET guarantees that updates are only reflected in related expressions.
The program shown on the left of Figure 3 conveys the intent that the value of the height variable h, and the expression that serves as the left operand of the binary expression h*2, are immutable. Consequently, any manipulation of the width will result in changes to the ratio value 2. If we further designate h*2 as const, as shown on the right of Figure 3, any manipulation of the width will lead to the transformation of the entire binary expression from const(h*2) to an updated expression const(h*2)+dh, where dh denotes the displacement value. This kind of transformation is not feasible in Sketch-n-Sketch.
Figure 3.
A manipulation-directed example in FuseSVG.
FuseSVG builds upon our previous work, FuseDM [9], while FuseDM laid the foundation for bidirectional live programming with an operation-based approach for fusing updates into the source program, FuseSVG refines and enhances this framework by addressing key limitations identified during the development of FuseDM. Specifically, FuseSVG introduces improvements in the handling of update propagation, correctness in positioning updates, and user-intended relationships within the program. These extensions make FuseSVG a more robust and flexible approach, offering finer control over program updates while maintaining consistency.
In summary, our contributions are as follows:
- We introduce a manipulation-directed programming approach where manipulations represent changes on the SVG. These changes are fused into programs to construct a desired program. Furthermore, a const marker is utilized to guide the propagation and fusion of the manipulation in the intended location.
- We design both the forward evaluation and the backward fusion semantics to preserve manipulations. We have proven that the language semantics satisfy the GetPut and WeakPutGet properties to guarantee correctness, thereby ensuing the validity of our approach.
- We have implemented a prototype, FuseSVG, that supports manipulation-directed programming for SVG. It has been tested with 14 nontrivial benchmark examples to demonstrate the effectiveness of our approach.
2. A Core Language DF
We initially describe the syntax of the core language DF, which stands for the functional language F with Deltas. The DF language is designed as a minimal core language for expressing key concepts of our system, particularly the manipulation of program deltas and the structured application of updates. It formalizes how delta values are handled, enabling precise reasoning about how changes propagate through a program. The purpose of DF is to provide an abstract environment in which the logic of program updates can be clearly modeled and analyzed. Next, we present its formal semantics and provide inference rules that determine whether a given expression is modifiable, which are used during the manipulation propagation process.
2.1. Syntax of DF
In Figure 4, we outline the core functional language, DF, designed for crafting source programs. Basic expressions are composed of constants c, variables x, abstractions , and applications . It also includes binary expressions, case expressions, and , which is used to described recursive functions. Constructors include tuple constructions and list constructions . The const keyword is introduced, which can be used to designate an expression as non-changeable (const e). Consequently, it includes a special expression that retains the delta for the expression and a special value that stores updates on the constant c, which are vital for demonstrating the correctness of our approach.
Figure 4.
Syntax of language DF.
Constants encompass numbers n, Booleans b, strings s, and empty lists []. Values are made up of constants with deltas , tuples , list constructions , and function closures . The environment E is responsible for mapping variables to their corresponding values v, while the environment maps variables to Boolean values . represents a special Boolean value that includes , , a tuple of , and a special value , which is internally used for lambda expressions. Basic delta operations consist of , replacement (repl), composition (), plus delta () that augment a given expression’s value, delete that signifies an element’s removal from the list, insert that indicates an element’s addition to the list, and an extra function closure used exclusively during computation. The subscript Δ is employed to differentiate the notations of the same operators in the arithmetic expression.
Example 1.
A list of simple examples.
Example 2.
Suppose the programmer stretches the rectangle horizontally by 10; the program defined in Figure 3 will be updated to the following one.
Note that a program augmented with deltas acts as an intermediate representation, which is crucial to our approach and will be explained in detail later. Delta values can be seen as a form of lifting, where updates (represented as deltas) are encapsulated within a computational context and applied to the source program, similar to how applicative functors apply functions to wrapped values. Specifically, delta values are combined and propagated through program expressions, preserving the program’s original structure and intent, much like how applicative functors combine computations in a functional context. However, all deltas are ultimately eliminated in the final program. The resulting program derived from Example 2 is shown below:
2.2. Semantics of DF
This section describes the semantics of DF as shown in Figure 5. While most of the evaluation rules are quite straightforward, we only explain those rules that involve the delta or const marker. Evaluating a const-marked expression is equivalent to directly evaluating the expression itself. When evaluating an expression with a delta denoted as , the expression e is first evaluated to a value v, resulting in a value . We impose a restriction on binary expressions and such that the delta can only occur on one side. This does not reduce the expressiveness of our language, as the delta only occurs on one side during update propagation in our setting.
Figure 5.
Evaluation rules for DF.
Example 3.
The evaluation result of the program in Example 2 is as follows, where the width value is set to 200, augmented with a delta of :
2.3. Updatability of Expression
When an expression is marked with const, the syntactic structure of the expression is not modifiable; however, the value of the expression may be subject to change if variables are present within the expression. To ascertain whether the computed value of an expression is updatable, we establish a set of inference rules. The const inference rules are outlined in Figure 6. If an expression is inferred as , it signifies that the expression is not updatable. Conversely, if it is inferred as , it implies that the expression is updatable.
Figure 6.
Inference rules for expression updatability.
As depicted in Figure 6, E signifies an environment for variables bound to their respective values, while represents a set containing variables bound to . For a given expression e, we infer it as to ascertain the updatability of the expression. Nearly every expression has two rules: one marked with const, and one without. If a constant is marked with const, it is inferred as . In the absence of the const keyword, it is inferred as . If the expression is inferred as , then the const-marked expression is also inferred as . The updatability of a variable depends on whether it is marked with the const keyword and the binding of the variable x in . However, if a variable x lacks the const keyword, it will invariably be inferred as , indicating that the expression x is modifiable. For a lambda expression, we create a tuple that encapsulates E, , and the lambda expression , which is utilized in lambda application. For a binary expression marked with const, we initially infer the Boolean value of the left and right operands when marked with the const keyword. The computation of is straightforward. Given that and can only be or , the result is if both and is , otherwise, it is . If it lacks the const keyword, we directly return . For a case expression, pattern is matched with to construct , which is subsequently used for evaluating . When a tuple is marked with const, we infer and when marked with const. The const inference rules for lists are the same as for tuples. When an expression contains delta, it will always be inferred as if it is not marked by const. Otherwise, the delta should be and the result is the same as evaluating e.
Example 4.
Given a lambda application, the environment E contains a variable y, and in the environment , the variable y is bound to , then the inferred result is .
Example 5.
Given a binary expression, the environment E contains variables x and y, and in the environment , the variable x is bound to , while the variable y is bound to . Even if the binary expression is marked by const, it is inferred as since y is inferred as .
3. A Direct Manipulation Language DM
We begin by introducing the syntax of the direct manipulation language DM, followed by a description of its semantics in detail.
3.1. Syntax of DM
The syntax of DM is outlined in Figure 7. The symbol is used to denote a delta, which is capable of expressing a variety of direct manipulations. It is important to note that the same notation is employed in Figure 4. In the core language, DF, the symbol signifies the basic deltas that can be incorporated into the source program. The identity delta, id, leaves the output unaffected, while the replacement delta, repl , substitutes the output value with the computed value from . The composition of two deltas, represented as , signifies the manipulation of on the output, followed by . The addition delta, , appends the computed value from to the output.
Figure 7.
Syntax of direct manipulation language DM.
The delta list constructor fabricates a list of deltas, , that operates on a list. The list deletion delta, delete n, eliminates the nth element in the list, while the list insertion delta, insert, introduces the computed value from as the nth element. The list modification delta, modify , updates the nth element by , and the list folding of delta, dfold, is analogous to foldl in Haskell. is a function, defined using the core language DF, that accepts the current accumulator, (an atomic expression), as input and derives a new accumulator, . In addition, it computes the arguments to be applied by the function , which computes a delta for each list element being recursively processed. The binding operation, denoted as , introduces a new variable x, extracts a sub-value from the output value using the selector , and then binds the variable x to that sub-value. The variable x is subsequently utilized in . The selector is designed to extract a specific value from a data structure. It includes the identity selector id, and composition selectors with projectors comprised of head, tail, fst, and snd. The atomic expression encompasses constants c, variables x, tuples, and lists. The arithmetic expression incorporates atomic expressions , additions , multiplications , and so forth.
Example 6
(Transforming a Rectangle into a Square). Consider a canvas with one rectangle, whose width is 200, and height is 100.
The operation to turn the rectangle into a square is written as follows: It first extracts the rectangle’s width using the selector and assigns it to the newly introduced variable w. Then, it modifies the rectangle’s h with w.
Example 7
(Turn Two Rectangles Horizontally Attached). Consider a canvas with two rectangles spread horizontally, but not attached. The operation to direct the second rectangle to attach to the first rectangle horizontally is written as follows: It extracts the first rectangle’s x and width using the selector and , and assigns it to the newly introduced variables x and w, respectively. Then, it modifies the second rectangle’s x with .
3.2. Semantics of Language DM
The semantics of DM are defined in Figure 8. The judgment indicates that “applying a delta to the value v results in ”. The definition of values v, which includes constants with delta, tuples, and lists, is provided in Figure 4.
Figure 8.
Semantics of delta language DM (selected rules).
The D-Id rule asserts that the identity id leaves the value v unchanged. The D-Repl rule introduces a new value, denoted as , derived from the arithmetic expression . It is important to note that the original value v is not directly replaced by . Instead, the change is recorded as a delta. eval is an auxiliary function that evaluates arithmetic expressions into values. The D-Com rule states that the composition is applied to v, resulting in a delta value that stores the composed deltas. The D-Add rule states that, when applying to a numerical value n, the value v computed from is not directly applied to the value n, but is stored in a delta value. The D-Tuple rule states that, when applying the tuple delta to the tuple value , (resp. ) is transformed by (resp. ) and becomes (resp. ). The D-Cons rule operates similarly to D-Tuple.
The application rules for deletion, insertion, and modification apply to list values. The D-Ins-1 rule states that when applying to a list construction and n is larger than zero, is transformed by and becomes . The D-Ins-2 rule states that when n is zero, the computed value marked with delta insert is inserted at the front of the list . The rules for deletion and modification follow a similar pattern. The D-Fold rule applies deltas to each element from left to right. With the recursion logic(i.e., ) and the delta generation (i.e., ), is an atomic expression denoting an accumulator, usually the list index. Let us use an example to show how the rule D-Fold works. The D-Binding rule is utilized for creating bindings from elements in the output. It initially introduces a new variable, denoted as x, to represent the value selected from an existing set using the function. The semantics of these selectors are elaborated in Figure 9. Subsequently, this selected value x is used to evaluate the change . This mechanism is employed in various applications such as defining alignment, joining points, and applying a format brush.
Figure 9.
Semantics of selectors for values ().
Example 8.
Consider a sequence of rectangles on the canvas. The following operation sets all the rectangles’ widths to 100. The command changes a specific rectangle’s width to 100. The dfold function iterates over a sequence of rectangles from left to right.
Example 9.
The operation below sets the first rectangle’s width to 20, the second rectangle’s width to 40, and so on. Initially, i is set to 1, and the variable is bound to 20. After one iteration, i is updated to 2, and the value of becomes 40.
4. Fusing Delta into the Program
We first introduce the well-behavedness properties, followed by an example to illustrate how fusion operates. Subsequently, we present the primary fusion algorithms that fuse delta into program. Particularly, when program expressions are marked with const, the fusion rules become more intricate.
4.1. Well-Behavedness
Our system incorporates a fundamental bidirectional transformation mechanism, which comprises the standard evaluation (get function) and the fusion (putback function). These components maintain consistency between source programs and their outputs by satisfying the GetPut law and the WeakPutGet law.
Lemma 1
(GetPut). If , then .
The GetPut law (stability) states that if we obtain an output from a program and perform no direct manipulation, the program must remain unchanged. This is straightforward because we default the delta modification to be the identity function (id), which keeps the same source program.
Lemma 2
(WeakPutGet). If , , and , , then .
The WeakPutGet law (soundness) is more critical. It states that when a delta is fused into a program, even if the output of executing the modified program differs, by collecting the changes and fusing them again into the original program, we should obtain the same modified program.
Theorem 1.
If a program satisfies the GetPut law and the WeakPutGet law, then the program is well behaved.
We need to emphasize that we use the WeakPutGet law instead of the PutGet law. The PutGet law is more restrictive, such that the output of executing the modified program is equal to the output generated by applying that delta to the original output. We relax it by allowing the updated outputs to change, which is important for reconstructing the correct relations in the output.
4.2. Fusion Example
Figure 10a depicts the fusion of a delta , which adds 2 to the output of a program. The fusion involves a depth-first traversal of the program’s structure, as illustrated in Figure 10b (where let-bindings are represented as function applications). The green lines, labeled with numbers, indicate the path of the propagation. Once the timesExp node is reached, the traversal proceeds to the left branch, and the delta transforms into . Ultimately, the delta is fused with the constant value 1 at the node highlighted in yellow. The final program is shown in the bottom of Figure 10a.
Figure 10.
Fusion example.
If we carefully analyze the propagation steps, it becomes apparent that there are other reasonable points for updating, rather than solely the “deepest” one. For instance, consider updating function definitions of plus or times. Since there are binary operations, we have the flexibility to update either operand. To address this, we introduce the const marker to indicate which parts we prefer to keep unchanged. For example, if we wish to preserve value of the let binding of the variable a and aim to update the plus function, we can mark the binding of a and the application times a as unmodifiable using const. The revised and updated program is presented as shown in Figure 11.
Figure 11.
Fusing to the const marked program.
4.3. Fusion Rules
Fusion rules are the core mechanisms that propagate deltas across the main program structure. The key idea is to propagate the delta back to variables or sub-expressions using variable bindings (environment). Given a delta , under value environment E, and Boolean environment , update expression e to , and update the value environment to . The fusion rules are depicted in Figure 12 and Figure 13.
Figure 12.
Propagation rules.
Figure 13.
Propagation rules (cont.).
- Constants. There are two rules for constant values. If a constant value is marked by the const keyword, it remains immutable (since the inference rule will always yield to true for a constant marked by the const keyword, only is allowed). If the constant value c is not marked by the const keyword, it can be updated to , as demonstrated in P-Con2. Note that we do not directly compute the result of applying to value c, but preserve the delta, which is important for proving the WeakPutGet law.
- Variable Expressions. There are four rules for variable expressions based on whether the variable expression x is marked by the const keyword and whether the bound value for the variable is modifiable. It is crucial to differentiate between the variable expression x and the variable binding for x. The first rule P-Var1 states that if the bound value for the variable is not modifiable and the variable expression is marked by const, then only the delta is permitted. The second rule P-Var2 states that if the bound value for the variable is not modifiable, but the variable expression is modifiable, then modify the expression to . The third rule P-Var3 states that if the bound value for the variable is modifiable, and the variable expression is marked by const, then the variable x in the environment E is bound to a new delta that composes with the original delta . The last rule P-Var4 states that if the bound value for the variable is modifiable, and the variable expression is not marked by const, then despite the fact that updating the variable expression (as in P-Var2) or the variable binding (as in P-Var3) is reasonable, updating the variable, which yields a result identical to P-Var3, is the preferred approach. The last rule P-Var4 is the same as the P-Var defined in FuseDM [9].
- Lambda Abstractions. The rule P-Lam1 states that, when applying the closure , the program becomes . Since the lambda abstraction is marked by the const keyword, the body of the lambda abstraction (e) cannot be modified; only the bindings of the variables in the environment are updated. The lambda abstraction in P-Lam2 is not marked by the const keyword, and the fusion can be decomposed into two updates: replacing the function body with and the environment with the updated one .
- Function Applications. The rule P-App1 states that, if the inference result of the application is true, the application is not modifiable. The rule P-App2 states that, if the inference result of expression is true, then only can be updated. When fusing delta to , even though there exists a possibility to update x, the fusion algorithm will refrain from updating x. We first evaluate to a lambda closure, a tuple of an environment , and a lambda abstraction . For simplicity, we assume only variable patterns in the lambda abstraction. Under the binding of variable x to , which is computed from and environment , we update the expression and environment to and , respectively. And finally, we use the updated expression and environment to update expression to , and environment E to . The rule P-App3 is a bit complicated, since both and can be updated, thus they may encounter conflicts. A merge operation is defined for resolving conflicts. The intuitive idea is explained by the example “fusing into the program ” in Table 1.Table 1. Detailed explanation of P-App3 with an example.
The operator in Step 7 first merges variable bindings obtained from fusion of and , and then embeds inconsistent deltas into (resp. ) to obtain (resp. ), which is formally defined in Section 4.4. The final program is as follows, which evaluates to .
- Case Expressions. The rule P-Case1 states that, if the inference result of the case expression is true, the expression is not modifiable. The rule P-Case2 states that, if the inference result of is true, then only the matched case branch expression is updated. The rule P-Case3 first applies the delta to the selected branch , resulting in the updated and . Then, it collects the delta from for , and subsequently fuses it into . Finally, the merge operator is used to resolve any conflicts.
- Binary Expressions. The rule P-C-Oplus1 states that if a binary expression marked with const is inferred as true, then the expression is not modifiable. The rule P-C-Oplus2 states that updating a const marked binary expression is the same as updating the binary expression with each operand marked by const. The rule P-Oplus1 states that if both and are inferred as true, then update the entire binary expression with . The rule P-Oplus2 states that if is inferred as true and is inferred as false, then update the right operand by a computed delta . The rule P-Oplus3 updates instead. The rule P-Oplus4 states that, if both and are inferred as false, then it chooses to update , but a merge operation is needed to resolve conflicts. How to apply deltas to binary expressions varies in different deployments; we give potential heuristic rule (P-Oplus4) for applying deltas to left operands. Other strategies include modifying the right operands, which can be achieved by marking the left operand with const.
- Tuples. The rule P-C-Tuple states that updating a const marked tuple is the same as updating each element marked by const. The rule P-Tuple states that one must update with and with . Since and may share variables, then a conflict-resolving operation is needed. The rule P-C-List (resp. P-List) is similar to the rule P-C-Tuple (resp. P-Tuple).
4.4. Merge Operation
Intuitively, the merge operator compares two structurally equivalent environments using the comparing operator and then embeds conflicting bindings into sub-expressions using the embedding operator ⊙. The merge operator and the comparing operator are the same as the definition in FuseDM, except the definition of embedding operator.
Definition 1
(Merge Operator). The merge operator reconciles conflicts of variable bindings as follows. holds if
Definition 2
(Comparing Operator). The comparing operator compares two environments and and returns the successfully merged part and two inconsistent parts ( and ).
Definition 3
(Embedding Operator). The embedding operator embeds deltas bound in the mapping around free variables in e, denoted by
where the “” operator replaces the free occurrences of the variable x in e with .
The embedding operator introduces deltas at the variable expression x, by substituting x with to encapsulate the delta at the variable expression x. This approach differs from the mechanism employed in FuseDM [9]. However, when the program with deltas is evaluated to a program without deltas, the same embedding mechanism as described in FuseDM is utilized.
4.5. Well-Behavedness
The well-behavedness (Theorem 1) properties includes the GetPut law and the WeakPutGet law. The GetPut law is straightforward because when the direct manipulation is the identity id, it then keeps the same source program as it is. Proving the well-behavedness is essentially the same as verifying the WeakPutGet law. We give a sketch of the proof for the WeakPutGet law.
Proof.
By induction on the fusion derivation below, we show the P-Con2, P-Var3, and P-App3 three cases as representatives:
- The P-Con2 case:
- (a)
- According to P-Con2, the updated program is .
- (b)
- By E-dv, .
- (c)
- The delta is .
- (d)
- By P-Con2, .
- (e)
- Since is the same as , is the same as , which is the goal.
- The P-Var3 case:
- (a)
- According to P-Var3, the updated program is the same as the original one, but the environment changes, .
- (b)
- By E-Const, and E-Var, .
- (c)
- The delta is .
- (d)
- By P-Var3, .
- (e)
- Since is the same as , is the same as , which is the goal.
- The P-App3 case:
- (a)
- According to P-, .
- (b)
- By the induction hypothesis, , , and .
- (c)
- By Lemma 4, and .
- (d)
- By (c) and Lemma 3, .
- (e)
- By E-App, .
- (f)
- By the induction hypothesis, , .
- (g)
- According to definition, and , the updated program is , which is the goal.
□
Definition 4
(Equivalent Environment). and are equivalent (denoted ) if they are structurally equivalent and , , and .
Lemma 3.
If and , then .
Lemma 4
(Merge Equivalency). If . implies .
Since Lemmas 3 and 4 are straightforward, we omit the details here.
5. Evaluation
The evaluation aims to analyze the expressiveness of the language to cover the scenario of SVG design. In addition, we present two typical benchmark examples in detail to show how an SVG graphic is constructed.
5.1. Expressiveness
FuseSVG introduces the const keyword, enabling users to mark expression to narrow down the appropriate location from multiple options, ensure certain constants in the program remain unaltered, and preserve relationships between SVG elements within the program. To illustrate the expressiveness of FuseSVG (FuseSVG is available at https://github.com/zanlyun/FuseDMI, accessed on 19 February 2025), we have replicated 14 benchmark examples and conducted an analysis from these three perspectives, as shown in Figure 14. All of these examples originate from FuseDM [9]. The results are summarized in Table 2. The term ‘Multiple update choice’ refers to situations where there are several possible places for making updates, such as the left or right operand of a binary operation. ‘Immutability for a single value’ suggests that the programmer intends to set certain constant values as non-updatable, like the x and y coordinates of a rectangle or the center of an ellipse. The ‘Number of relations’ corresponds to the count of relation expressions defined in the program. For instance, the width of a rectangle being twice its height () is considered a relation.
Figure 14.
Benchmarks.
Table 2.
Analysis of benchmarks.
Based on our analysis of the 14 benchmark examples, all examples encounter the problem of selecting suitable sub-expressions for updating when fusing deltas into the program. All examples necessitate the setting of certain constant values as non-updatable. For example, in the Precision Floor Plan example, there are three relations: the y coordinate of the second rectangle is identical to that of the first rectangle; the x coordinate of the second rectangle should be the sum of the first rectangle’s x coordinate and its width w; and both rectangles share the same height h. Relation expressions are defined across all examples and can be viewed as an important property in SVG design, emphasizing the importance of defining the invariance of relations in the source program.
Now, let us perform a detailed comparison between FuseDM and FuseSVG using the Precision Floor Plan Example. Listings 1 and 2 show the actual code for FuseDM and FuseSVG, respectively, which generates the output SVG shown in Figure 14(1). From a code perspective, compared to FuseDM, FuseSVG introduces const markers to specify that certain relations should be preserved, ensuring that certain expressions cannot be updated.
| Listing 1. Precision Floor Plan Example in FuseDM. |
| main = let rect1_x=175 in let rect1_y=239 in let rect1_width=60 in let rect1_height=106 in [ rect[0,219,rect1_x,rect1_y,rect1_width,rect1_height], rect[0,30,rect1_x+rect1_width,rect1_y,rect1_width*2,rect1_height] ]; |
| Listing 2. Precision Floor Plan Example in FuseSVG. |
| main = let rect1_x=175 in let rect1_y=239 in let rect1_width=60 in let rect1_height=106 in { rect{0,219,const rect1_x,rect1_y,rect1_width,rect1_height}, rect{0,30,const(rect1_x+rect1_width),rect1_y,const rect1_width*2,rect1_height} }; |
When generating the SVG, we have a specific intent: Suppose the left-hand rectangle is rect1, and the right-hand one is rect2. For instance, rect2’s width is defined as twice rect1’s width (rect1_width * 2), and rect2 adheres to rect1, where rect2’s x is rect1’s x plus rect1_width, and rect2’s y is the same as rect1’s y.
Next, we perform three update operations on both FuseDM and FuseSVG, as shown in Figure 15. First, if we shrink rect2’s width by 16, FuseDM modifies rect2’s width expression to , which breaks the intended relationship. However, in FuseSVG, this update proportionally adjusts rect1’s width, preserving the relationship by shrinking it from 60 to 52.
Figure 15.
A comparison between FuseDM and FuseSVG.
Similarly, if we shrink rect1’s width by 13, the expression for rect1_width becomes . In FuseDM, this breaks the relationship where rect2’s width is always twice rect1’s width. However, in FuseSVG, the width of rect2 is proportionally adjusted, preserving the intended relationship between the two rectangles by updating rect1_width to 47.
Finally, when we move rect2 horizontally by +10, FuseDM updates the expression to , breaking the alignment. In contrast, FuseSVG updates rect1_x directly, ensuring that the relative positioning of the rectangles is preserved. If we further want to keep rect1_x stable, we can mark rect1_x as const, ensuring that only rect1_width is updated.
In summary, FuseDM propagates updates to constants whenever possible while guaranteeing the PutGet property. However, this can sometimes lead to situations where relationships between elements are broken during updates, particularly when elements are interdependent or when updates occur in unintended locations. On the other hand, FuseSVG introduces more sophisticated mechanisms, such as using the const keyword for immutability, which helps preserve these relationships while still allowing for flexible updates. This ensures that changes to one element do not unintentionally disrupt the overall structure of the program.
5.2. Case Study
Let us use two benchmark examples to show how an SVG graphic is constructed step-by-step.
Battery Example: Initially, the canvas is empty, and the program assigns an empty list ([]) to a main variable. To draw a battery, we first draw a rectangle as shown in Figure 16a. This action automatically generates a piece of program, as shown on the right side, which is a main function that contains a list with the rectangle we just drew. The number 120 represents the color of the rectangle, while 119 and 146 are the x and y coordinates of the rectangle, respectively. The values 178 and 76 represent the width and height of the rectangle, respectively. By using direct manipulation, we eliminate the need to write the definition of the rectangle, especially the actual value. Sketching on the canvas is more intuitive than writing code. Subsequently, we draw another rectangle adjacent to the first one, as illustrated in Figure 16b. This action generates a delta, which is then fused into the program.
Figure 16.
A step-by-step guide to constructing a battery.
Although we placed the second rectangle next to the first one, the generated code does not inherently reflect this intention. The x value of the second rectangle (296) is very close to the sum of the x value and the width of the first rectangle (297). Therefore, we can refactor the code to establish relations by (1) abstracting values as variables and (2) constructing relations and marking them as non-modifiable with the const keyword. The final result is presented in Figure 16c. Now, regardless of whether we move or resize the first or second rectangle, the relations are consistently preserved.
Balance Scale Example: Figure 17 outlines the steps to construct a balanced scale (for more details, please refer to the video at the provided URL https://youtu.be/ImoOpGfubBw, accessed on 19 February 2025). After drawing the vertical red line in the second step, it is not possible to verify if the line is truly vertical just by looking at the SVG. Therefore, we need to check the code and adjust the y value to ensure it is truly vertical. Next, a refactoring step is needed to establish the relationship between the ellipse created in the first step and the vertical line. In fact, after each drawing step, a refactoring step is necessary to build the relationships, particularly the addition of the const marker, to the generated code. The construction of the balanced scale involves 20 steps and results in 26 lines of code. Since the relationships are well defined and marked as non-modifiable, any movement of an element in the figure will lead to changes in related elements.
Figure 17.
A step-by-step guide to constructing a balanced scale.
6. Related Work
6.1. Bidirectional Transformations
Bidirectional transformations [10] serve as a mechanism for maintaining consistency between a source and a corresponding view, which is derived from that source. The programming language community has conducted extensive research on bidirectional transformations [11,12,13]. Existing approaches to bidirectional transformations have primarily focused on transformations over a variety of structured data [14]. This includes lists [15], trees [11,16,17], graphs [18,19], and relational databases [20]. Matsuda et al. [21] proposed FliPpr, a system designed to derive parsers from pretty-printers. None of the existing approaches permit modifications to the transformation program itself. In other words, updates to the view must remain within the scope of the forward transformation. However, our work, FuseSVG, supports updating the program to a new one. Cheney et al. [22] discussed the principle of least change for bidirectional transformations. They posited that a bidirectional transformation should only make changes to the artifact that are strictly necessary, which is a principle that can guide the design of backward transformations, but it does not allow for the updating of the bidirectional program.
6.2. Ambiguity and Updatability
Sketch-n-Sketch [4] employs heuristics to resolve ambiguities and introduces an approach based on trace-based program synthesis. This method tracks the origin of constants within the program and establishes value-trace equations with the manipulated output. These equations are subsequently solved using a constraint solver. As mentioned by the author:“Our approach utilizes heuristics to automatically resolve ambiguities, aiming to achieve a balance between interactivity and predictability.” For instance, consider the value-trace equation . This equation can be solved by arbitrarily altering the value of either or .
Bidirectional Evaluation [7], Bidirectional Preview [23], and BiOOP [24], typically, introduce a backward update evaluation that propagates the updated output value to program constants by retracing the forward evaluation steps. Sketch-n-Sketch [6] provides semi-automated SVG programming via direct manipulation, and it is developed to further provide full output-directed programming for SVG [8]. During which, a freeze annotation, written !, is introduced to tell Sketch-n-Sketch not to change those constants marked by ! when shapes are moved on the canvas. The freeze operator is formally defined in the LITTLELEO language [7]. For a given expression marked by freeze (), evaluation of this expression is the same as evaluating e, but for updating; if expression e evaluates to v, then only v can be propagated back, which means the value of the expression cannot be changed.
The motivation for introducing the keyword const differs from that of the freeze operation. While the freeze operation prohibits the changing of the value computed from the expression, our const keyword restricts the modification of the expression syntactically; however, the value can still be changed.
Let us assume that the output of the Sketch-n-Sketch program on the left, shown in Listing 3, represents the value of h. This output value is immutable, as the expression used to calculate h is marked with a freeze annotation(!). In contrast, within our system, FuseSVG, the const keyword is used to keep the expression 2*w static, while allowing for the update of the width w. The program is shown in Listing 4. The design goal of const is used to keep the form of the expression unchanged, which is useful for keeping the relation between two or more variables. The freeze totally prohibits any update to the variable in the expression. When const operates on constant values, it also does not allow modification of the constant value expression, which is, in fact the same as the freeze operation in Sketch-n-Sketch.
| Listing 3. Sketch-n-Sketch. |
| w = 50 h = !(2*w) |
| Listing 4. FuseSVG. |
| w = 50 h = const (2*w) |
FuseDM [9] adopts an operation-based approach that propagates deltas. This method can modify not only constant literals in programs but also alter program structures by integrating direct manipulations into the ‘proper position’. FuseDM has the same ambiguity issue as the state-based approaches. The application rules for fusing delta into addition expression A-Add and multiplication expression A-Mul defaults to update the left operand. Our work FuseSVG extends FuseDM by providing a const keyword, to describe user intention of which part in the program shall not be modified, to reduce the ambiguity and gain more control over the program when being updated.
6.3. Constraints on View
There are scenarios such that an output may include derived values that are calculated from other components of the output, necessitating that the output be consistently updated. For instance, if a rectangle’s width is twice as its height, the width is determined based on the height. The dependency problem on the output is discussed in the putback-based bidirectional transformation language BiGUL [25,26]; since in bidirectional transformation the output is the view, it is called view dependency. A constructor named accepts an additional function to describe the relation of two view elements. During backward transformation, the function is computed to check whether the two elements are consistent with the relation.
Several visual design systems [27,28,29,30] integrate constraint specification. Apparatus [30] is a hybrid graphics editor and programming environment, which is used for creating interactive diagrams. Upon drawing a rectangle, one can utilize arithmetic expressions to define the value of the attributes. For instance, Scale X is defined to be twice the value of Scale Y, as depicted in Figure 18. Consequently, stretching the rectangle either horizontally or vertically results in changes to Scale Y, while the constraint remains intact.
Figure 18.
A glimpse of Apparatus.
7. Conclusions
This paper presents a bidirectional live programming tool for SVG manipulation, which is capable of controlling the updatability of expressions by marking them with the const keyword. Moreover, by designating the relation expression as unmodifiable using the const keyword, it enables the update of other related elements to re-establish the relation on the output when a related element in the output is altered. From a theoretical perspective, we relaxed the PUTGET property to the WeakPUTGET property, facilitating the reconstruction of relations on the output. We ensure that both the evaluation semantics and the fusion algorithm comply with the well-behavedness properties. To demonstrate the adaptability of our approach, we have effectively implemented 14 benchmark examples that illustrate a variety of updatability scenarios.
Looking ahead, there are two potential areas for improvement. Firstly, although the relation can currently be constructed in the output, it is directly fused into the source program. A more efficient approach would be to store the relation in the output as well. This would allow for the corresponding update of a related element when one element changes, ensuring the consistency of relations in both the program and the output. Secondly, the task of inferring the inherent relations programmed in the source to the output needs to be addressed. Additionally, the delta language could potentially be extended to incorporate recursive drawing [31]. This technique, which employs recursive algorithms to generate complex, often fractal-like, graphics, could significantly enhance the versatility and expressiveness of the delta language in graphical representation.
Author Contributions
Conceptualization, T.Z., X.H. and Z.H.; methodology, T.Z., X.Z., X.H. and Z.H.; software, T.Z. and X.Z.; validation, T.Z., X.Z. and X.H.; investigation, T.Z., X.Z. and X.H.; writing–original draft preparation, T.Z., X.Z. and X.H.; writing–review and editing, T.Z., X.Z., X.H. and Z.H. All authors have read and agreed to the published version of the manuscript.
Funding
This work was supported by the Natural Science Foundation of Fujian Province for Youths (Grant No. 2021J05230) and the Doctor-Initiated Research Fund of Longyan University (Grant No. LB2020010).
Institutional Review Board Statement
Not applicable.
Informed Consent Statement
Not applicable.
Data Availability Statement
Data are contained within the article.
Conflicts of Interest
The authors declare no conflicts of interest.
References
- McDirmid, S. Living it up with a live programming language. In Proceedings of the 22nd Annual ACM SIGPLAN Conference on Object-Oriented Programming Systems, Languages and Applications, OOPSLA ’07, Montreal, QC, Canada, 21–25 October 2007; pp. 623–638. [Google Scholar] [CrossRef] [Scilit]
- Burckhardt, S.; Fahndrich, M.; de Halleux, P.; McDirmid, S.; Moskal, M.; Tillmann, N.; Kato, J. It’s alive! continuous feedback in UI programming. SIGPLAN Not. 2013, 48, 95–104. [Google Scholar] [CrossRef] [Scilit]
- McDirmid, S. The promise of live programming. In Proceedings of the 2nd International Workshop on Live Programming, LIVE, Rome, Italy, 2016; Volume 16. [Google Scholar]
- Chugh, R.; Hempel, B.; Spradlin, M.; Albers, J. Programmatic and direct manipulation, together at last. In Proceedings of the 37th ACM SIGPLAN Conference on Programming Language Design and Implementation, Santa Barbara, CA, USA, 13–17 June 2016. [Google Scholar] [CrossRef] [Scilit]
- Shneiderman. Direct Manipulation: A Step Beyond Programming Languages. Computer 1983, 16, 57–69. [Google Scholar] [CrossRef] [Scilit]
- Hempel, B.; Chugh, R. Semi-Automated SVG Programming via Direct Manipulation. In Proceedings of the 29th Annual Symposium on User Interface Software and Technology, UIST ’16, Tokyo, Japan, 16–19 October 2016; pp. 379–390. [Google Scholar] [CrossRef] [Scilit]
- Mayer, M.; Kuncak, V.; Chugh, R. Bidirectional Evaluation with Direct Manipulation. Proc. ACM Program. Lang. 2018, 2, 1–28. [Google Scholar] [CrossRef] [Scilit]
- Hempel, B.; Lubin, J.; Chugh, R. Sketch-n-Sketch: Output-Directed Programming for SVG. In Proceedings of the 32nd Annual ACM Symposium on User Interface Software and Technology, UIST ’19, New Orleans, LA, USA, 20–23 October 2019; pp. 281–292. [Google Scholar] [CrossRef] [Scilit]
- Zhang, X.; Xie, R.; Guo, G.; He, X.; Zan, T.; Hu, Z. Fusing Direct Manipulations into Functional Programs. Proc. ACM Program. Lang. 2024, 8, 1211–1238. [Google Scholar] [CrossRef] [Scilit]
- Czarnecki, K.; Foster, J.N.; Hu, Z.; Lämmel, R.; Schürr, A.; Terwilliger, J.F. Bidirectional Transformations: A Cross-Discipline Perspective. In Theory and Practice of Model Transformations; Paige, R.F., Ed.; Springer: Berlin/Heidelberg, Germany, 2009; pp. 260–283. [Google Scholar]
- Foster, J.N.; Greenwald, M.B.; Moore, J.T.; Pierce, B.C.; Schmitt, A. Combinators for Bi-Directional Tree Transformations: A Linguistic Approach to the View Update Problem. In Proceedings of the 32nd ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL ’05, Long Beach, CA, USA, 12–14 January 2005; pp. 233–246. [Google Scholar]
- Fischer, S.; Hu, Z.; Pacheco, H. The essence of bidirectional programming. Sci. China Inf. Sci. 2015, 58, 1–21. [Google Scholar] [CrossRef] [Scilit]
- Hofmann, M.; Pierce, B.; Wagner, D. Symmetric Lenses. In Proceedings of the 38th Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, POPL ’11, Austin, TX, USA, 26–28 January 2011; pp. 371–384. [Google Scholar]
- Ko, H.S.; Hu, Z. An Axiomatic Basis for Bidirectional Programming. Proc. ACM Program. Lang. 2017, 2, 1–29. [Google Scholar] [CrossRef] [Scilit]
- Barbosa, D.M.; Cretin, J.; Foster, N.; Greenberg, M.; Pierce, B.C. Matching Lenses: Alignment and View Update. In Proceedings of the 15th ACM SIGPLAN International Conference on Functional Programming, ICFP ’10, Baltimore, MD, USA, 27–29 September 2010; pp. 193–204. [Google Scholar]
- Hu, Z.; Mu, S.C.; Takeichi, M. A programmable editor for developing structured documents based on bidirectional transformations. High.-Order Symb. Comput. 2008, 21, 89–118. [Google Scholar] [CrossRef] [Scilit]
- Zhu, Z.; Ko, H.S.; Zhang, Y.; Martins, P.; Saraiva, J.; Hu, Z. Unifying Parsing and Reflective Printing for Fully Disambiguated Grammars. New Gener. Comput. 2020, 38, 423–476. [Google Scholar] [CrossRef] [Scilit]
- Hidaka, S.; Hu, Z.; Inaba, K.; Kato, H.; Matsuda, K.; Nakano, K. Bidirectionalizing Graph Transformations. In Proceedings of the 15th ACM SIGPLAN International Conference on Functional Programming, ICFP ’10, Baltimore, MD, USA, 27–29 September 2010; pp. 205–216. [Google Scholar]
- Hidaka, S.; Asada, K.; Hu, Z.; Kato, H.; Nakano, K. Structural Recursion for Querying Ordered Graphs. In Proceedings of the 18th ACM SIGPLAN International Conference on Functional Programming, ICFP ’13, Boston, MA, USA, 25–27 September 2013; pp. 305–318. [Google Scholar]
- Tran, V.D.; Kato, H.; Hu, Z. Programmable View Update Strategies on Relations. Proc. VLDB Endow. 2020, 13, 726–739. [Google Scholar] [CrossRef] [Scilit]
- Matsuda, K.; Wang, M. FliPpr: A System for Deriving Parsers from Pretty-Printers. New Gener. Comput. 2018, 36, 173–202. [Google Scholar] [CrossRef] [Scilit]
- Cheney, J.; Gibbons, J.; McKinna, J.; Stevens, P. On principles of Least Change and Least Surprise for bidirectional transformations. J. Object Technol. 2017, 16, 3:1–3:31. [Google Scholar] [CrossRef] [Scilit]
- Zhang, X.; Hu, Z. Towards Bidirectional Live Programming for Incomplete Programs. In Proceedings of the 44th International Conference on Software Engineering, ICSE ’22, Pittsburgh, PA, USA, 22–27 May 2022; pp. 2154–2164. [Google Scholar] [CrossRef] [Scilit]
- Zhang, X.; Guo, G.; He, X.; Hu, Z. Bidirectional Object-Oriented Programming: Towards Programmatic and Direct Manipulation of Objects. Proc. ACM Program. Lang. 2023, 7, 230–255. [Google Scholar] [CrossRef] [Scilit]
- Ko, H.S.; Zan, T.; Hu, Z. BiGUL: A formally verified core language for putback-based bidirectional programming. In Proceedings of the 2016 ACM SIGPLAN Workshop on Partial Evaluation and Program Manipulation, PEPM ’16, St. Petersburg, FL, USA, 18–19 January 2016; pp. 61–72. [Google Scholar] [CrossRef] [Scilit]
- Hu, Z.; Ko, H.S. Principles and practice of bidirectional programming in BiGUL. In Bidirectional Transformations: International Summer School, Oxford, UK, July 25–29, 2016, Tutorial Lectures; Springer: Cham, Switzerland, 2018; pp. 100–150. [Google Scholar]
- Jackiw, R.N.; Finzer, W.F. The geometer’s sketchpad: Programming by geometry. In Watch What I Do: Programming by Demonstration; MIT Press: Cambridge, MA, USA, 1993; pp. 293–307. [Google Scholar]
- Xia, H.; Araujo, B.; Grossman, T.; Wigdor, D. Object-Oriented Drawing. In Proceedings of the 2016 CHI Conference on Human Factors in Computing Systems, CHI ’16, San Jose, CA, USA, 7–12 May 2016; pp. 4610–4621. [Google Scholar] [CrossRef] [Scilit]
- Jacobs, J.; Gogia, S.; Mundefinedch, R.; Brandt, J.R. Supporting Expressive Procedural Art Creation through Direct Manipulation. In Proceedings of the 2017 CHI Conference on Human Factors in Computing Systems, CHI ’17, Denver, CO, USA, 6–11 May 2017; pp. 6330–6341. [Google Scholar] [CrossRef] [Scilit]
- Schachman, T. Apparatus. Available online: http://aprt.us (accessed on 17 February 2025).
- Bricault, S. Recursive Drawing. Available online: http://bricault.mit.edu/recursive-drawing (accessed on 17 February 2025).
Disclaimer/Publisher’s Note: The statements, opinions and data contained in all publications are solely those of the individual author(s) and contributor(s) and not of MDPI and/or the editor(s). MDPI and/or the editor(s) disclaim responsibility for any injury to people or property resulting from any ideas, methods, instructions or products referred to in the content. |
© 2025 by the authors. Licensee MDPI, Basel, Switzerland. This article is an open access article distributed under the terms and conditions of the Creative Commons Attribution (CC BY) license (https://creativecommons.org/licenses/by/4.0/).

















