Making Bubbling Practical

08/24/2018 ∙ by Sergio Antoy, et al. ∙ Portland State University 0

Bubbling is a run-time graph transformation studied for the execution of non-deterministic steps in functional logic computations. This transformation has been proven correct, but as currently formulated it requires information about the entire context of a step, even when the step affects only a handful of nodes. Therefore, despite some advantages, it does not appear to be competitive with approaches that require only localized information, such as backtracking and pull-tabbing. We propose a novel algorithm that executes bubbling steps accessing only local information. To this aim, we define graphs that have an additional attribute, a dominator of each node, and we maintain this attribute when a rewrite and/or bubbling step is executed. When a bubbling step is executed, the dominator is available at no cost, and only local information is accessed. Our work makes bubbling practical, and theoretically competitive, for implementing non-determinism in functional logic computations.

READ FULL TEXT VIEW PDF
POST COMMENT

Comments

There are no comments yet.

Authors

page 1

page 2

page 3

page 4

This week in AI

Get the week's most popular data science and artificial intelligence research sent straight to your inbox every Saturday.

1 Introduction

Consider, in Curry’s syntax [15], a function for computing the body mass index [21] of an individual:

bmi x = weight x / (height x) ^ 2

(1)

In a functional logic language, for example, to find someone with a bmi greater than 25, we evaluate (solve) the following disequation:

bmi > 25

(2)

where is a non-deterministic choice among the members of some set of interest. For example, for the sake of completeness and simplicity let us assume that the set of interest consists of Alice and the parents of Bob, i.e., Alice ? parent Bob. Weights, heights and parents of individuals are defined by suitable functions.

The expressions manipulated by a program are abstracted by graphs. Fig. 1 shows one of these graphs, a state of the computation of disequation (1).

@C=-2pt@R=15pt@C=5pt & & & > @-[dll] @-[drr]
& / @-[dl] @-[dr] & & & & 25
weight @-@/_9pt/[ddr] & & ^ @-[dl] @-[dr]
& height @-[d] & & 2
& ? @-[dl] @-[dr]
Alice & & parent @-[d]
& & Bob

Figure 1: Graphical representation of a state of the computation of disequation (1) for .

We draw the attention on some elements of this expression/graph, hereafter referred to as , that are relevant to our discussion. The subexpression of rooted by “?” is a non-deterministic choice, or more simply a choice. Its values are the values of either of its alternatives. The rest of is the context of the choice. The node of labeled by “/” is a dominator of the choice, i.e., any path from the root of to the choice goes through the dominator.

The focus of this paper is how to compute the values of an expression of this kind. To obtain all the values of the expression, all the alternatives of the choice must be considered. Each alternative is evaluated within the context of the choice. Since the computation for a selected alternative “consumes” the context, we need a fresh/new context for each alternative.

Historically, three main techniques have been proposed for this problem. Backtracking [17] first selects an alternative of the choice and computes the entire expression with this alternative, then it rollbacks the computation to recreate the context before selecting the other alternative. By contrast, pull-tabbing [2, 10, 12] and bubbling [3, 4] “clone”, though in different ways, the context for each alternative, so that each alternative can be computed in its own context. Our work focuses on bubbling. Loosely speaking, a bubbling step “swaps” a choice with some dominator of the choice and in the process clones the portion of the graph between dominator and choice to provide a context to each alternative of the choice. A formal definition and a detailed example will be provided later.

Bubbling, as currently formulated, has some advantage over the other techniques, but also has a fatal flaw: the necessity of traversing the entire expression to find the immediate dominator of the choice anytime a bubbling step is executed. In this paper, we describe a novel technique that stores some additional information in a graph, and maintains it during a computation, so that when a bubbling step is executed, a dominator, which may not be immediate but is still viable, is readily available without traversing the graph.

This paper is organized as follows. Section 2 is a mini-introduction to Functional Logic Programming and Curry. Section 3 provides background information about the kind of programs our contribution applies to, the definition of graphs which slightly extends the standard one with additional information in the nodes, the kind of steps allowed in a computation, and the definition of bubbling which also extends the standard one with additional information. Section 4 presents two original algorithms, one to execute a bubbling step using only local information, the other to execute a rewrite step that preserves information used by the previous algorithm. Section 5 states the correctness of the original algorithms, but without formal proofs. Only an informal argument is provided that hopefully helps understanding the inner working of the algorithms. Section 6 summarizes work in this area and Section 7 offers our conclusion.

2 Functional Logic Programming and Curry

We briefly recall elements of functional logic languages and Curry that may help understanding our contribution. More details can be found in surveys [6, 14] and the language report [15].

Curry is a declarative multi-paradigm language combining functional and logic programming with a syntax close to Haskell’s [19]. A Curry program declares constructor symbols via data declarations and operation (or function) symbols via defining rules. The following example shows these elements:

data Bintree = Leaf | Branch Int Bintree Bintree
isin - Leaft = False
isin n (Branch i left right)
XXXX= n == i || isin n left || isin n right

(3)

Leaf and Branch are the constructors of a type binary tree of integers and isin is an operation telling whether an integer is in a tree. The operators “==” and “||”, which stand for Boolean equality and conjunction, are defined in a standard library. A function may be applied to a logic (or free) variable which is instantiated non-deterministically by narrowing, if needed. For example, the evaluation of:

isin 5 x where x free

(4)

binds to Leaf and Branch u v w non-deterministically, where , and are free variables as well.

Non-determinism originates from free variables, as shown above, and overlapping rules, the epitome of which are those of the choice operation:

x ? - = x
- ? y = y

(5)

The textual order of rules is irrelevant. Each rules of the choice is equally applicable. Thus, the expression evaluates to and with the value non-deterministically chosen.

Curry has a variety of other syntactic and semantic features usually found in a general-purpose programming language. These features are not needed to understand our contribution and we only mention a few ones for the sake of completeness: the compile-time definition of infix binary operators with user-defined precedence and associativity; higher-order functions, but without higher-order narrowing; declarative input/output in the monadic style; set functions for encapsulated non-determinism; and functional patterns. Curry programs can be partitioned into modules for programming in the large and they can invoke external functions, i.e., functions that are not coded in Curry, for interacting with the operating system.

The mainstream compiler/interpreter of Curry, Pakcs [16], provides an extensive set of libraries, including primitives for building graphical user interfaces, interactive web pages, distributed applications and accessing database engines.

3 Background

The theory of graph rewriting [13, 20] is heavy. It would be impossible to review here even the simplest and most fundamental concepts. Therefore, we only recall those elements of the theory, in particular their notations, that are necessary to present our contribution.

The core representation of a program in current Curry compilers [7, 11, 16] is a Limited Overlapping Inductively Sequential, abbreviated LOIS, graph rewriting system. In LOIS systems, the rules are left-linear and constructor-based. The left-hand sides of the rules are organized in a hierarchical structure called a definitional tree [1]. In LOIS systems, the only operation with overlapping rules is the choice defined in (2).

We ignore free (also called logic) variables in the rest of our discussion since they can be replaced by generators [5]. Thus, narrowing in a LOIS system with free variables is equivalent to rewriting in a similar LOIS system without free variables.

Non-determinism in functional logic programming comes from both free variables and overlapping rules. Since free variables are banned and LOIS systems allow a single overlapping rule, the choice operation is the only source of non-determinism in our programs. Bubbling handles the evaluation of expression with occurrences of the choice operation, hence all the non-determinism of our programs.

3.1 Graphs

A graph is the formalization of the intuitive and often informal notion of expression. Our definition of a graph is a minor extension of [13] from which we entirely adopt notations and terminology. We add a new attribute, , intended to map every non-root node, , of a graph to some proper dominator of . We will say that the attribute of a graph is correct iff for every node of , is indeed a dominator of . We recall that given a graph and two nodes and , dominates iff every path from the root of to contains . It follows that every node trivially dominates itself. Throughout this paper, when we say that dominates , or is a dominator of , we mean properly, i.e., , unless when explicitly stated otherwise. A consequence is that a dominated node is never the root of a graph. A node may have many dominators, in particular, the root of a graph dominates every other node of . Typically, will be the immediate (closest) dominator of , but we do not enforce this condition because it is convenient to relax it in some situations. We will see later that a bubbling step clones the nodes in a path from a dominator node to a dominated node . Therefore, the closer is to , the fewer nodes are cloned in a bubbling step at . The bubbling algorithm also uses the predecessor relation, which is the inverse of the successor relation, hence we do not explicitly define a predecessor attribute. Occasionally we will equate a node of a graph with the subgraph of rooted by since they are in a bijection.

Definition 1 (Expression)

Let be a signature, a countable set of variables, a countable set of nodes. A graph or expression over is a 5-tuple such that:

  1. is the set of nodes of ;

  2. is the labeling function mapping each node of to a signature symbol or a variable;

  3. is the successor function mapping each node of to a possibly empty string of nodes of such that if , where , and (for the following condition, we assume that a variable has arity zero) , then there exist in such that ;

  4. is a distinguished node of called the root of ;

  5. is the dominator function mapping every non-root node of to some node such that is a dominator of ;

  6. if and and , then , i.e., every variable of labels one and only one node of ; and

  7. for each , either or there is a path from to , i.e., every node of is reachable from the root of .

Typically we say “expression” when talking about programs and “graph” when making formal claims. All our graphs are “term graphs” in the terminology of [13], i.e., they have exactly one root. Expressions have both a textual and a graphical notation. The textual notation is in Curry’s syntax [15]. The notation presents node labels rather than the nodes themselves. Function application defines the successor relation, e.g., in , the root of is a successor of the root of the entire expression which is labeled by . A where clause introduces local declarations including variables. “Variable” is an overloaded concept in Curry. There are bound variables, introduced in the left-hand side of a rule, logic variables, introduced by a free declaration, and local variables, introduced by let or where constructs. A principal use of these constructs is for sharing nodes. For example, in the graph of Fig. 1 the node labeled by the choice is the successor of two distinct nodes that “share” it. The textual representation of the graph is:

weight x / (height x) ^ 2 > 25
XXXXwhere x = Alice ? parent Bob

(6)

The “where” clause introduces the local variable . The variable identifies a node. The binding of the variable defines the subexpression rooted by this node. Node identifiers are arbitrary and irrelevant to most purposes. In fact, graphs that differ only for a renaming of nodes [13, Def. 15] are considered equal.

3.2 Computations

Sections 2 and 3 of [13] formalize key concepts of graph rewriting such as replacement, matching, homomorphism, rewrite rule, redex, and step in a form ideal for our discussion. Therefore, we adopt these definitions in their entirety, including their notations. Similar treatments are discussed in [8, 9, 20]. All these treatments do not include the dominator attribute, which is specific to our work. Hence, after some preliminaries, we will formally describe how the dominator attribute is maintained throughout a computation.

A computation of an expression in a program is a sequence such that is a rewrite step according to a rule of . Later in this paper we will also allow a second kind of step called bubbling [3, 4] which is independent of the rules of a program. “Evaluation” is a synonym of a computation in which the intent is to find a value (constructor normal form) of an expression. A rewrite step is the replacement in a graph of an instance of a rewrite rule’s left-hand side (the redex) with the corresponding instance of the rule’s right-hand side (the contractum or replacement). If is a subexpression of an expression , the context of in is the portion of disjoint from . Choice reductions, which are non-deterministic steps, are limited to the root of an expression where a choice has an empty context. This makes it easy to evaluate both alternatives concurrently and independently. By contrast, reducing a choice with a non-empty context entails an irrevocable commitment, since there is a single context for two alternatives Bubbling steps are equivalent to non-deterministic steps in the sense that they have the potential to produce exactly the intended results of an expression [4], but without any irrevocable commitment to either alternative.

3.3 Bubbling

Fig. 2 informally shows a bubbling step. The step “moves” a node labeled by the choice up to a dominator and clones the paths from the dominator to the choice for each alternative of the choice. We begin with an auxiliary concept.

@C=-2pt@R=10pt@C=5pt & g @.[dd]

& d 
@.@/_8pt/[dd] @.@/^8pt/[dd]

& ?
@-[ld] @-[rd]
x 
& & y                           @C=-2pt@R=10pt@C=5pt & g @.[dd]

& ?
@-[ld] @-[rd]
d 
@.@/_8pt/[dd] @.@/^8pt/[dd] & & d @.@/_8pt/[dd] @.@/^8pt/[dd]

x 
& & y 

Figure 2: Schematic representation of the bubbling transformation. A bubbling step transforms the left-hand side graph into the right-hand side one. Node is the root. In the left-hand side, node is a dominator of the node labeled by “?”. Dotted lines stand for paths. and are the roots of arbitrary expressions. The paths from to the choice are cloned by the transformation.
Definition 2 (Partial renaming)

Let be a term graph over , a subset of and a set of nodes disjoint from . A partial renaming of with respect to and is a bijection such that:

(7)

Similar to substitutions, we call and , the domain and image of , respectively. We overload to graphs as follows: is a graph over such that:

  • ,

  • , for all ,

  • ,
    iff , for all ,

  • ;

  • , for all non-root .

In simpler language, is equal to , except that the nodes in have been renamed with a “fresh” name in . In particular, the dominator of a renamed node is the renamed dominator of .

Although intuitively simple, a formal definition of the bubbling relation is non-trivial [3]. To ease understanding, we split it into two parts. Part I [4] defines the transformation and implicitly defines the labeling and successor mapping through the expression notation of graphs. Part II, which is novel, defines the dominator mapping which is not present in [3, 4] and cannot be inferred from the notation.

Definition 3 (Bubbling - part I)

Let be a graph and a node of such that the subgraph of at is of the form , i.e., . Let be a dominator of in and the set of nodes that are on some path from to in , including and , i.e., , where is the set of all paths from to in . Let and be partial renamings of with domain and disjoint images. Let , for . The bubbling relation on graphs is denoted by “” and defined by , where the root node of the replacement of at is obviously fresh. We call and the origin and destination, respectively, of the bubbling step, and we denote the step with “” when this information is relevant.

Definition 4 (Bubbling - part II)

Let and be graphs such that , for some nodes and of . For each node of , we define by cases as follows.

  • node is created by the bubbling transformation.

    • , where is the root (labeled by the choice) of the replacement at in : .

    • and , i.e., properly renames some node of according to some renaming , i.e., is a node in a path from down to the choice. We consider two cases:

      • , i.e., renames the destination of the step and consequently is a successor of : .

      • : .

  • node persists from to :

    • let and . We consider three cases:

      • is the left (resp. right) successor of : , where left (resp. right) instance of .

      • : .

      • : .

This definition is sensible, i.e., if is correct, then is correct. Later, we will provide an informal proof of this claim.

@C=-2pt@R=15pt@C=5pt & & & & & & &> @-[dlll] @-[drr]
& & & & ? @-[dlll] @-[drr] & & & & & 23
& / @-[dl] @-[dr] & & & & & / @-[dl] @-[dr]
weight @-@/_9pt/[ddr] & & ^ @-[dl] @-‘d[r]‘[rrrrrrd] & & & weight @-@/_9pt/[ddr] & & ^ @-@/^5pt/[dr] @-[dl]
& height @-[d] & & & & & height @-[d] & & 2
& Alice & & & & & parent @-[d]
& & & & & & Bob

Figure 3: Result of executing a bubbling step on the state the computation, say , of Figure 1. For any node of on a path from the dominator to choice, such as the node labeled by weight, the bubbling step creates two new nodes with the same label in the result. We call these nodes “clones” of .

4 Algorithms

A non-deterministic step executed by bubbling must compute a dominator, ideally the immediate one, of a node in a graph, . Computing the immediate dominator of a node in requires the traversal of [18]. This requirement makes bubbling too expensive when a non-deterministic step occurs in a large context. To avoid this cost, we attach to each node of a dominator of , ideally the immediate one, and we maintain this attribute during a computation. This attribute allows us to execute bubbling steps using only readily available, local information.

bubble XXlet be a fresh node XX? XX XX XXfor in {left, right}, do XXXXclear map XXXXtraverse XXXXadd map to XXXXmap XXXXreplace map with XXreplace with traverse XXif is mapped, then return XXlet be a fresh node XXmap XX XXif , then XXXXfor in , do XXXXXXtraverse XXXXXXadd to XXXX XXfor in , do XXXXif is not mapped, then
Figure 4: Bubbling step algorithm. This algorithm executes a bubbling step that preserves the correctness of the dominator attribute. An explanation is in the flowing text. The step visits only nodes in the path(s) from the source, , to the destination, .

The procedures in Fig. 4 execute bubbling steps. Traverse, which is in the scope of bubble, executes a depth-first upward partial traversal of , starting at some node . The procedure terminates upon either visiting a node already visited, or visiting . Since, by definition, is on every path from the root of to , traverse terminates for every node .

An execution of traverse clones each node on each path from to . A function, map, maps each node being cloned to its clone. Traverse clones a node producing , recursively clones all the paths from to each predecessor of , installs as their successor, and sets label and dominators of . If dominates a node which is not mapped, and therefore is outside the portion of being cloned by traverse, then will not be the dominator of after the bubbling step. This is because a second clone of is created by another invocation of traverse. Thus, the dominator of is set to node , which is created by procedure bubble, discussed next. This situation is exemplified by the node labeled by in Fig. 3.

Procedure bubble executes a bubbling step at some node . Its main activity, delegated to traverse, is to clone the portion of between and twice. Let’s call these clones and . The remaining activities are “gluing” together the pieces according to the definition of a bubbling step. A fresh node, , labeled by “?” is the predecessor of and . The instruction replace p with q means that every incoming edge to is redirected [13, Def. 8] to and that . The bottom-most nodes of and are the clones of . These nodes are discarded and replaced by the left and right arguments of . Successor and dominator attributes are adjusted as needed.

Our algorithm uses the inverses of both the dominator and successor relations. Hence, dominance should be implemented bidirectionally. In order for the bubbling algorithm just described to work, we must maintain the dominator attribute of a graph as rewriting steps are executed. The initial top-level expression typically has only a few nodes; sometimes it is just “main.” For this reason initializing the attribute is trivial.

When a rewrite step is executed, for some graphs and , some nodes of are missing in and typically some nodes that were not in appear in . A consequence is that the attribute of some nodes in may be missing or may be incorrect. For this reason, after executing a rewrite step, it is required to set the attribute of these nodes. This is achieved by executing the procedure fix_dominator.

The notion of redex is crucial in the following discussion, but how to determine the redex of step is not a part of the discussion. Choosing the redex of step is the job of an evaluation strategy, but our algorithm is independent of the strategy.

Let be a graph, be the root of the redex, and be the rule of the step. Node is not labeled by choice, otherwise we would execute a bubbling step. The step replaces the redex at with a replacement rooted by some node . The redex pattern [9, Def. 2.7.3] at consists of the set of nodes of matched by non-variable nodes of . Any node in the set is erased unless it is shared (there is an edge incoming to the node from a node outside the redex pattern). By analogy, we call the set of nodes created by the step the contractum pattern (even though it is not a pattern). Any node in the set corresponds to a non-variable node of . The dominator-preserving rewriting algorithm is presented in Fig 5.

fix_dominator x0.   let be the root of the contractum; x1.   for each node in the redex pattern at , erased by the step, ; x2.   for each node in the contractum pattern at , excluded , ; x3.   ; x4.   for every other node of (and ), ;
Figure 5: Dominator preserving step algorithm. After a rewrite step at , the algorithm associates a dominator to the nodes affected by the step. Any node dominated by a node that is erased by the step becomes dominated by the root of the contractum. Any node created by the step becomes dominated by the root of the contractum. The root of the contractum becomes dominated by the dominator of the root of the redex. The dominator of any other node is unchanged.

Procedure fix_dominator sets or updates the attribute of nodes that are created or nodes whose dominator is erased by a rewrite step, respectively. For all other nodes, if the attribute is correct before the rewrite step, then it remains correct after the rewrite step. Later, we will provide an informal proof of this claim.

Procedure fix_dominator may not always produce the immediate dominator of some node. Consider the following rule and expression:

f x y = h y
f (g z) (g z) where z = 0

(8)

where , and are operations and , and variables. The expression rewrites to h (g 0). The dominator of 0 is set, by our algorithm, to h whereas the immediate dominator is g. Since contractum patterns are typically shallow and with few nodes, the dominator set by the algorithm for any node will typically be rather close to .

5 Correctness

The correctness of our algorithm is stated in propositions 1 and 2. We will present each proposition and sketch a short proof idea. This is not meant to be a formal, rigorous proof, but rather to give some intuition on why these algorithms perform as intended.

Both of these proofs rely on more elementary facts about dominators. First, removing edges from a graph does not change the dominance relation as long as the graph remains connected. Second, if dominates , then adding the edge to a graph rooted by will remove a dominator iff there is a path that does not contain . Third, if dominates , then dominates every node on every path from to . The first proposition states that a rewriting followed by the execution of procedure fix_dominator preserves the correctness of the dominator attribute.

Proposition 1 (Correctness of fix_dominator)

Let be a graph such that the dominator attribute of is correct and be a redex of . If is the graph obtained from by first executing a rewrite at , and then executing fix_dominator, then the dominator attribute of is correct.

Let be an arbitrary node of . The proof is by cases on the relative positions of , , and . There are 4 cases to consider. If is erased by the rewrite, then the root of the contractum is a dominator of in . If is the root of the contractum, then is a dominator of in . If is any other node in the contractum pattern, then the root of the contractum is a dominator of in . If is any other node, then is a dominator of in . This last case follows from the fact that, if does not dominate , then the dominance relation is trivially preserved, and if it does dominate , then it will dominate the contractum, and therefore will dominate .
The second proposition states that procedure bubble preserves the correctness of the dominator attribute.

Proposition 2 (Correctness of bubble)

Let be a graph such that the dominator attribute of is correct and is a node of labeled by “?”. Let be the graph obtained from by executing bubble(). Then the dominator attribute of is correct.

Let be the set of nodes on a path from to . Let , where is either left or right, be a renaming created in the bubbling step. The bubbling step creates two subgraphs and isomorphic to . Let be the predecessor in of the roots of and . As in Prop. 1, we show the correctness by cases. Let be an arbitrary node in . If , the source of the bubbling step, is a dominator of in . If , then is a dominator of in since is the only predecessor of . If both and are in , then the dominator of the renaming of in is the renaming of the dominator of in . That is, . This follows directly from the fact that is isomorphic to . If , but , then is a dominator of in , since the bubbling steps creates two “copies” of . If neither nor are in , then is a dominator of in . This final case is similar to the final case of the previous proposition. If doesn’t dominate then the dominance relation is trivially preserved, but if it does dominate then it will dominate , so the dominance relation will still be preserved.

6 Related Work

Several techniques have been proposed to execute non-deterministic steps in functional logic programming. Backtracking [17] arbitrarily chooses either alternative of a choice. If the computation of the chosen alternative fails to find a value, the computation continues with the other alternative. Otherwise, if and when the computation of the chosen alternative completes, the result is presented to the user and the computation continues with the other alternative. Copying is a naive approach to the problem of a single context for the two alternatives of a choice. It “moves” the choice to the root of the expression being evaluated by cloning the path(s) from the root down to the choice. In one clone, the choice is replaced by one of its alternatives. In the other clone it is replaced by the other alternative. The two clones are evaluated independently and simultaneously, e.g., by means of interleaved steps. Copying is not used in practice, but is refined by bubbling and pull-tabbing described next.

Bubbling [3, 4] attempts to improve the efficiency of copying by “moving” the choice to a dominator of the choice. Only the paths between the dominator and the choice are cloned. The upfront number of cloned nodes is reduced. If the computation of one clone fails, the choice “disappears” and the overall amount of cloning is reduced. Pull-tabbing [2, 10, 12] attempts to improve the efficiency of bubbling by “moving” the choice to a predecessor of the choice. There is no path to clone, but in some cases the step would be unsound. For this reason an additional piece of information, called a fingerprint, is attached to nodes to ensure that subexpressions resulting from one alternative of a choice are not mixed with subexpressions resulting from the other alternative of the same choice.

All the above techniques have drawbacks—some serious. Backtracking may fail to produce the results of the second alternative of a choice if the computation of the first alternative does not terminate. Copying may needlessly clone a long path that remains largely unused if either alternative of a choice quickly fails. Bubbling, before our formulation, required traversing the entire expression being evaluated to find a suitable dominator of the choice [18]. Pull-tabbing has a substantial overhead and must bring every choice to the top of an expression, even after an alternative of the choice fails.

The usual application of dominators is to optimizing control flow graphs, which are static. Bubbling and rewriting keep updating the expression being evaluated. Hence, our work explores novel applications of graph dominance.

7 Conclusion

We propose an original algorithm that executes bubbling steps accessing only local information. To this aim we add a dominator attribute to the graph’s nodes. We also extend the notions of rewriting and bubbling for maintaining the attribute during a computation. The attribute allows us to execute a step with an effort independent of the size of the context in which the step occurs.

We roughly estimate the overhead of our approach as follows. Most steps of a computation are deterministic, hence rewrites. The cost of a rewrite is that of pattern matching and executing replacements. Pattern matching is unaffected by our extensions. Replacements consist in allocating nodes and setting their attributes. A drawback of our approach is the presence of additional attributes in the nodes, which must be set. In plausible representations of expressions, regardless of our approach, a node has several attributes such as label and successors. We add dominator and predecessors. A gross estimate is that our approach doubles the number of attributes that must be allocated and set. Processing the attributes of a node is only a fraction of the work of rewriting, hence we estimate that the overhead of maintaining the dominator is a factor less than 2 both for memory allocation and execution time.

Our work suggests that a compiler of a functional logic language, whose backend generates bubbling code, is not only feasible, but possibly competitive. Bubbling has some unique advantages over other techniques for the implementation of non-determinism: completeness w.r.t. backtracking, reduced cloning w.r.t. copying, and no overhead for failures w.r.t. pull-tabbing. Future work should aim at a more formal and detailed proof of the correctness of our algorithms and at an implementation to assess their competitiveness.

References

  • [1] S. Antoy. Definitional trees. In H. Kirchner and G. Levi, editors, Proceedings of the Third International Conference on Algebraic and Logic Programming, pages 143–157, Volterra, Italy, September 1992. Springer LNCS 632.
  • [2] S. Antoy. On the correctness of pull-tabbing. TPLP, 11(4-5):713–730, 2011.
  • [3] S. Antoy, D. Brown, and S. Chiang. Lazy context cloning for non-deterministic graph rewriting. In Proceedings of the 3rd International Workshop on Term Graph Rewriting, Termgraph’06, pages 61–70, Vienna, Austria, April 2006.
  • [4] S. Antoy, D. Brown, and S.-H. Chiang. On the correctness of bubbling. In F. Pfenning, editor, 17th International Conference on Rewriting Techniques and Applications, pages 35–49, Seattle, WA, August 2006. Springer LNCS 4098.
  • [5] S. Antoy and M. Hanus. Overlapping rules and logic variables in functional logic programs. In Proceedings of the Twenty Second International Conference on Logic Programming, pages 87–101, Seattle, WA, August 2006. Springer LNCS 4079.
  • [6] S. Antoy and M. Hanus. Functional logic programming. Comm. of the ACM, 53(4):74–85, April 2010.
  • [7] S. Antoy and A. Jost. A new functional-logic compiler for curry: Sprite. CoRR, abs/1608.04016, 2016.
  • [8] F. Baader and T. Nipkow. Term Rewriting and All That. Cambridge University Press, 1998.
  • [9] M. Bezem, J. W. Klop, and R. de Vrijer (eds.). Term Rewriting Systems. Cambridge University Press, 2003.
  • [10] B. Brassel. Implementing Functional Logic Programs by Translation into Purely Functional Programs. PhD thesis, Christian-Albrechts-Universität zu Kiel, 2011.
  • [11] B. Braßel, M. Hanus, B. Peemöller, and F. Reck. KiCS2: A new compiler from Curry to Haskell. In Proc. of the 20th International Workshop on Functional and (Constraint) Logic Programming (WFLP 2011), pages 1–18. Springer LNCS 6816, 2011.
  • [12] B. Brassel and F. Huch. On a tighter integration of functional and logic programming. In APLAS’07: Proceedings of the 5th Asian conference on Programming languages and systems, pages 122–138, Berlin, Heidelberg, 2007. Springer-Verlag.
  • [13] R. Echahed and J. C. Janodet. On constructor-based graph rewriting systems. Technical Report 985-I, IMAG, 1997. Available at http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.31.4948.
  • [14] M. Hanus. Functional logic programming: From theory to Curry. In Programming Logics - Essays in Memory of Harald Ganzinger, pages 123–168. Springer LNCS 7797, 2013.
  • [15] M. Hanus (ed.). Curry: An integrated functional logic language (vers. 0.9.0). Available at http://www.curry-language.org, 2016.
  • [16] M. Hanus (ed.). PAKCS 1.14.3: The Portland Aachen Kiel Curry System. Available at http://www.informatik.uni-kiel.de/~pakcs, March 04, 2017.
  • [17] D. E. Knuth. Fundamental Algorithms, volume 1 of The Art of Computer Programming. Addison-Wesley, Reading, Massachusetts.
  • [18] T. Lengauer and R. E. Tarjan. A fast algorithm for finding dominators in a flowgraph. ACM Trans. Program. Lang. Syst., 1(1):121–141, January 1979.
  • [19] Simon Peyton Jones, editor. Haskell 98 Language and Libraries. Cambridge University Press, 2003.
  • [20] D. Plump. Term graph rewriting. In H.-J. Kreowski H. Ehrig, G. Engels and G. Rozenberg, editors, Handbook of Graph Grammars, volume 2, pages 3–61. World Scientific, 1999.
  • [21] Wikipedia contributors. Body mass index — Wikipedia, the free encyclopedia, 2018. [Online; accessed 3-August-2018].