=Paper=
{{Paper
|id=None
|storemode=property
|title=Nondeterministic Programming in Java with JSetL
|pdfUrl=https://ceur-ws.org/Vol-1068/paper-l14.pdf
|volume=Vol-1068
|dblpUrl=https://dblp.org/rec/conf/cilc/RossiB13
}}
==Nondeterministic Programming in Java with JSetL==
Nondeterministic Programming in Java with JSetL Gianfranco Rossi and Federico Bergenti Dipartimento di Matematica e Informatica, Università di Parma {gianfranco.rossi | federico.bergenti}@unipr.it Abstract. JSetL is a Java library that endows Java with a number of facilities that are intended to support declarative and constraint (logic) programming. In this paper we show how JSetL can be used to support general forms of nondeterministic programming in an object-oriented framework. This is obtained by combining different but related facili- ties such as logical variables, set data structures, unification, along with a constraint solver that allows the user to solve nondeterministic con- straints, as well as to define new constraints using the nondeterminism handling facilities provided by the solver itself. Thus, the user can de- fine her/his own general nondeterministic procedures as new constraints, letting the constraint solver handle them. The proposed solutions are il- lustrated by showing a number of concrete Java implementations using JSetL, including the implementation of simple Definite Clause Gram- mars. Keywords. Nondeterministic programming, Constraint Programming, Set-based Programming, Java language. 1 Introduction The problem of incorporating constructs to support nondeterminism into pro- gramming languages has been discussed at length in the past. Early references to this topic are [4] for a general overview, and [11] for an analysis of the prob- lem in the context of functional programming languages. Logic programming languages, notably Prolog, strongly rely on nondeterminism. Their computa- tional model is inherently nondeterministic (at each computation step, one of the clauses unifying a given goal is selected nondeterministically) and the pro- grammer can exploit and control nondeterminism using the language features when defining her/his own procedures. As regards imperative programming, however, only relatively few languages provide primitive constructs to support nondeterminism. An early example is SETL [10], a Pascal-like language endowed with sets, which provides, among others, a few built-in features to support backtracking (e.g., the ok and fail primitives). More recently, the programming language Alma-0 [1] [2] provides a comprehensive collection of primitive constructs to support nondeterministic 212 Gianfranco Rossi and Federico Bergenti programming, such as the statements orelse, some, forall, commit, for creating choice points and handling backtracking. Also Python’s yield mechanism—and, more generally, the coroutining mechanisms present in various programming lan- guages—can be used as a way to explore the computation tree associated with a nondeterministic program. Our goal in this paper is to explore the feasibility of a library-based ap- proach to support nondeterministic programming in an object-oriented language. Specifically, our proposal is to exploit the nondeterministic constraint solver pro- vided by JSetL [9], a Java library that combines the object-oriented program- ming paradigm of Java with valuable concepts of CLP languages, such as logical variables, partially specified list data structures, unification, constraint solving. Using this library the programmer can define nondeterministic procedures by exploiting either the nondeterminism embedded in the predefined constraints (in particular, in set constraints), or the possibility to define new user-defined nondeterministic constraints and handle them through the built-in constraint solver. We will illustrate our solution with a number of simple examples using Java with JSetL. The paper is organized as follows. In Section 2 we show how JSetL can support nondeterminism through the use of built-in nondeterministic features, such as set constraints and the labeling mechanism. Section 3 briefly introduces general nondeterministic control structures and the relevant language constructs. In Section 4 we show how different nondeterministic control structures can be implemented in Java using the facilities for defining new constraints provided by JSetL. Finally, in Section 5 we show a more complete example of application of the facilities offered by JSetL to support nondeterministic control structures: the implementation of Definite Clause Grammars. 2 Embedded Nondeterminism A convenient way to express nondeterminism in JSetL is by means of set con- straints. As a matter of fact, nondeterminism is strongly related to the notion of set and set operations (see, e.g., [13] and [7]). Sets can be defined in JSetL as instances of the class LSet. Elements of a LSet object can be of any type, including other LSet objects (i.e., nested sets are allowed). Moreover, sets denoted by LSet (also referred to as logical sets) can be partially specified, i.e., they can contain unknown elements, as well as an unknown part [3]. Single unknown elements are represented by unbound logical variables (i.e., uninitialized objects of the class LVar), whereas the unknown part of the set is represented by an unbound logical set (i.e., an uninitialized object of the class LSet). For example, the three statements: LVar x = new LVar(); LSet r = new LSet(); LSet s = r.ins(x); Nondeterministic Programming in Java with JSetL 213 create an unbound logical variable x, an unbound logical set r, and a partially specified logical set s with an unknown element x and an unknown rest r (i.e., {x | r}, using a Prolog-like notation). JSetL provides the basic operations on this kind of sets, such as equality (viz., set unification [6]), inequality, membership, cardinality, union, etc., in the form of primitive constraints, similarly to what provided by the Constraint Logic Programming language CLP(SET ) [5]. A JSetL constraint solver is an instance of the class SolverClass. Basically, it provides methods for adding constraints to its constraint store (e.g., the method add) and to prove satisfiability of a given constraint (methods check and solve). If solver is a solver, Γ is the constraint stored in its constraint store (possibly empty), and c is a constraint, then solver.check(c) returns false if and only if Γ ∧ c is unsatisfiable. Solving equalities, as well as other basic set-theoretical operations, over par- tially specified sets may involve nondeterminism. For example, the equation {x, y} = {1, 2}, where x and y are unbound logical variables, admits two dis- tinct solutions: x = 1 ∧ y = 2 and x = 2 ∧ y = 1. In JSetL, these solutions are computed nondeterministically by the constraint solver, using choice points and backtracking. In the following example we exploit the nondeterminism embedded in set operations to provide a nondeterministic solution to the problem of printing all permutations of a set of integer numbers s. The problem can be modelled as the problem of unifying a (partially specified) set of n = |s| logical variables {x1 , . . . , xn } with the set s, i.e., {x1 , . . . , xn } = s. Each solution to this problem yields an assignment of (distinct) values to variables x1 , . . . , xn that represents a possible permutation of the integers in s. Example 1. (Permutations) public static void allPermutations(LSet s) { int n = s.getSize(); // the cardinality of s LSet r = LSet.mkLSet(n); // r = {x1 ,x2 ,...,xn } solver.check(r.eq(s)); // r = s do { r.printElems(’ ’); System.out.println(); } while (solver.nextSolution()); } The invocation LSet.mkLSet(n) creates a set consisting of n unbound logical variables. This set is unified, through the constraint eq, with the set of n integers s. This is done by invoking the method check of the current constraint solver solver (solver is assumed to be created outside the method allPermutations). The invocation check(r.eq(s)) causes a viable assignment of values from s to variables in r to be computed. Values in r are then printed on the standard output by calling the method printElems. Calling the method nextSolution allows checking whether the current con- straint admits further solutions and possibly computing the next one. This 214 Gianfranco Rossi and Federico Bergenti method exploits the backtracking mechanism embedded in the constraint solver: calling nextSolution forces the computation to go back until the nearest open choice point is encountered. Specifically, in the above example, solving r.eq(s) nondeterministically computes a solution to the set unification problem involv- ing the two sets r and s. Thus, all possible rearrangements of the values in the given sets (i.e., all possible permutations) are computed and printed, one at a time. The example illustrates also how the nondeterminism of the JSetL solver interacts with the usual features of the underlying imperative Java language. Note that in this example nondeterminism is implemented simply by oper- ations on sets and the nondeterministic search is completely embedded in the constraint solver. Since the semantics of set operations is usually well under- stood and quite intuitive, making nondeterministic programming the same as programming with sets can contribute to make the (non-trivial) notion of non- determinism easier to understand and to use. Whenever the problem at hand can be formulated as a Constraint Satisfaction Problem (CSP) over Finite Domains, solutions can be computed nondeterminis- tically by exploiting the so-called labeling mechanism. Values to be assigned to variables of the CSP are picked up nondeterministically from the variable do- mains: if the selected assignment turns out to be not suitable, another alternative is then explored. In JSetL this is obtained by using the constraint label. Solving the constraint s.label(), where s is a collection of logical variables, forces the program to nondeterministically generate an admissible assignment of values to variables in s, starting from the first variable in s and the first value in its domain (default labeling strategy in JSetL). This assignment is propagated to all the constraints in the constraint store and if none of them turns out to be unsatisfiable, then an assignment for the next variable in s is computed and propagated, and so on. As soon as a constraint in the store turns out to be unsatisfiable, backtracking occurs and a new assignment for the lastly assigned variable is computed. If a viable assignment for all the variables in s is finally found, then it represents a solution for the given CSP. For example, the well-known n-queens problem, very often used as a sample problem for illustrating nondeterministic programming, can be easily modelled as a CSP and solved using constraints over Finite Domains and a final labeling phase to nondeterministically generate all possible solutions. Unfortunately, not all problems whose solutions are naturally formulated as nondeterministic algorithms are also easily modelled as CSP. There are situations in which, in particular, the variable domains are difficult to bring under those supported by existing CP solvers, making the programming effort to model them in terms of the existing ones too cumbersome and sometimes quite ad hoc. On the other hand, the use of sets and set operations to model nondeterministic computations, as shown in this section, is not always feasible and/or convenient. Nondeterministic Programming in Java with JSetL 215 In conclusion, there are cases in which some more general programming ab- stractions to express and handle nondeterminism are required. We address this problem in the next sections. 3 Nondeterministic Control Structures Dealing with general nondeterministic control structures requires primarily the ability to express and handle choice points and backtracking. This implies, first of all, that the notion of program computation is extended to allow distinguishing between computations that terminate with success and computations that ter- minate with failure. Basically, a computation fails whenever it executes, either implicitly or explicitly, a fail statement. Conversely, a finite, error-free com- putation succeeds if it does not fail. In response to a failure, the computation backtracks to the last open choice point. Choice points may be created by the programmer using suitable language constructs, such as the following orelse statement (borrowed from [1]): either S1 orelse S2 . . . orelse Sn end which expresses a nondeterministic choice from among n statements S1 . . . Sn . More precisely, the computation of the orelse goes as follows: statement S1 is executed first; if, at some point of the computation (possibly beyond the end of the orelse statement) a failure occurs, then backtracking takes place and the computation resumes with S2 in the state it had when entering S1 ; if a new failure occurs, then the computation backtracks and it resumes with S3 , and so on; if a failure occurs after executing Sn and no other open choice points do exist, then the computation definitively fails. Let us briefly illustrate how to deal with general nondeterministic control structures with a simple example written using a C-like pseudo-language en- dowed with the orelse statement and a few other facilities to support nonde- terministic programming. In the next section we will show how the same control structures can be implemented in Java with JSetL. Given a list l of strings, split (all) the elements of l into two lists l1 and l2, such that the total length of the strings in l1 is equal to the total length of the strings in l2. For example, if l is ["I","you","she","we","you","they"] a possible splitting of l is l1 = ["I","they","you"] (total length = 8) and l2 = ["she","we","you"] (total length = 8), whereas if "she" is replaced by "he" no splitting is feasible. Note that we are assuming that l can contain repeated elements and that strings can be picked up from l in any order. The problem can be solved by defining a function split that nondeterministically splits l into two lists l1 and l2, and then forcing split to generate (via backtracking) all possible pairs of lists l1 and l2 until a pair respecting the given condition is found. An implementation of this algorithm written in pseudo-code using the orelse construct is shown in Example 2. Example 2. (List splitting—in pseudo-code) 216 Gianfranco Rossi and Federico Bergenti split(l): either l is []; return h[],[]i; orelse x is the first element and r the rest of l; hr1, l2i = split(r); return h[x | r1], l2i; orelse x is the first element and r the rest of l; hl1, r2i = split(r); return hl1, [x | r2]i; end; where [x | r1] (resp., [x | r2]) represents the list which is obtained by adding x as the first element to the list r1 (resp., r2). Therefore, if l is not the empty list, its first element is nondeterministically added to either the first sublist (second orelse alternative) or to the second sublist (third orelse alternative). If sumLength(l) is a function that computes the total length of all the strings in the list l, then the given problem is simply solved by calling split(l) and then requiring that the results of sumLength(l1) and sumLength(l2) are equal, that is: hl1, l2i = split(l); sumLength(l1) == sumLength(l2); Note that we are assuming that, in our pseudo-language, whenever an ex- pression e is used as a statement, such as, for instance, sumLength(l1) == sumLength(l2) or l is [], the following operational semantics is enforced: if e evaluates to true then continue; else fail.1 Therefore, if the pair hl1, l2i computed by split(l) does not satisfy the condition sumLength(l1) == sumLength(l2), then the computation backtracks to split and tries another open alternative created by the orelse statement, as long as at least one such alternative does exist. This example shows also the typical interleaving between nondeterminism and recursion: each recursive call to split opens three branches in the nonde- terministic computation of split. Executing the first orelse alternative, which represents the base of the recursion, corresponds to reaching a leaf in the com- putation tree, i.e. a possible solution. The domain of discourse, in this example, is that of lists of strings. Moreover we do not make any assumption on the length of the lists, on the presence of duplicated elements in them, and on the length of the strings composing them. Trying to encode this domain in terms of the usual constraint domains and trying to restate the problem as a CSP, e.g. over the domain of integer numbers, though feasible in principle, may lead to rather involved programs in practice. On the other hand, trying to restate that problem as a set-theoretical one, in order to 1 This goes much like the “boolean expressions as statement” feature of Alma-0 [1]. Nondeterministic Programming in Java with JSetL 217 exploit the nondeterminism involved in set constraints as shown in Section 2, may be hindered, at least, by the presence of duplicates in the input sequence. As mentioned in Section 1, very few programming languages support the above mentioned nondeterministic constructs as primitive features. Extending the language with primitive constructs that offer such support is, indeed, quite demanding in general. Our goal in this paper is to explore the feasibility of a library-based approach, where features to support nondeterministic programming are implemented on the top of an high-level language, namely Java, by exploiting the language abstraction mechanisms, without requiring any modification to the language itself. We will illustrate this solution in the next sections with a number of simple examples, using the Java library JSetL. 4 Implementing Nondeterministic Control Structures in JSetL As shown in Section 2, JSetL embeds nondeterminism at various levels. In par- ticular, set constraints, as well as the labeling mechanism, are inherently nonde- terministic. Availability of built-in nondeterministic constraints, however, is not sufficient to ensure the general kind of nondeterminism we would like to have. The solution that we propose in order to circumvent such difficulties is based on the availability in JSetL of a nondeterministic constraint solver and the pos- sibility for the programmer to define her/his own (nondeterministic) constraints. Those methods that require the use of nondeterminism are defined as new user- defined constraints. Within these methods the programmer can exploit facilities offered by JSetL for creating and handling choice-points. When solving these constraints the solver will explore the different alternatives using backtracking. User-defined constraints in JSetL are defined as part of a user class that extends the abstract class NewConstraintsClass. The actual implementation of user-defined constraints requires some programming conventions to be respected, as shown in the following example. Example 3. (Implementing new constraints) Define a class MyOps which offers two new constraints c1(o1,o2) and c2(o3), where o1, o2, o3 are objects of type t1, t2, and t3, respectively. public class MyOps extends NewConstraintsClass { public MyOps(SolverClass currentSolver) { super(currentSolver); } public Constraint c1(t1 o1, t2 o2) { return new Constraint("c1", o1, o2); } public Constraint c2(t3 o3) { return new Constraint("c2", o3); 218 Gianfranco Rossi and Federico Bergenti } protected void user_code(Constraint c) throws Failure, NotDefConstraintException { if (c.getName().equals("c1")) c1(c); else if(c.getName().equals("c2")) c2(c); else throw new NotDefConstraintException(); } private void c1(Constraint c) { t1 x = (t1)c.getArg(1); t2 y = (t2)c.getArg(2); //implementation of constraint c1 over objects x and y } private void c2(Constraint c) { t3 x = (t3)c.getArg(1); //implementation of constraint c2 over object x } } The one-argument constructor of the class MyOps initializes the field solver of the super class NewConstraintsClass with (a reference to) the solver cur- rently in use by the user program. The other public methods simply construct and return new objects of class Constraint. This class implements the atomic constraint data type. All built-in constraint methods implemented by JSetL (e.g., eq, neq, in, etc.) return an ob- ject of class Constraint. Each different constraint is identified by a string name (e.g., "c1"), which can be specified as a parameter of the constraint constructor. The method user code, which is defined as abstract in NewConstraints- Class, implements a “dispatcher” that associates each constraint name with the corresponding user-defined constraint method. It will be called by the solver during constraint solving. Finally, the private methods, such as c1 and c2, provide the implementation of the new constraints. These methods must, first of all, retrieve the constraint arguments, whose number and type depend on the constraint itself. We will show possible implementations of such methods (using nondeterminism) in Examples 4 and 6. Once objects of the class containing user-defined constraints have been cre- ated, one can use these constraints in the same way as the built-in ones: user- defined constraints can be added to the constraint store using the method add and solved using the SolverClass methods for constraint solving. For example, executing the statements MyOps myOps = new MyOps(solver); solver.solve(myOps.c1(o1,o2)); first creates an object of type MyOps, called myOps, then it creates the constraint "c1" over two objects o1 and o2 by calling myOps.c1(o1,o2), finally it adds this constraint to the constraint store and solves it by calling the method solve Nondeterministic Programming in Java with JSetL 219 of solver. Solving the constraint "c1" will cause the solver to call the concrete implementation of the method user code provided by myOps, and consequently to execute its method c1.2 User-defined constraints in JSetL can implement nondeterministic procedures by exploiting special features offered by the JSetL constraint solver. Defining nondeterministic constraints in JSetL, however, requires some additional con- siderations to be taken into account. First of all note that methods defining user-defined constraints must nec- essarily return an object of type Constraint. Thus, any other result possibly computed by the method must be returned through parameters. The use of unbound logical objects, i.e., logical variables as well as logical sets and lists, as arguments of the user-defined constraints provides a simple solution to this problem. More generally, the use of logical objects is fundamental in JSetL when deal- ing with nondeterminism. As a matter of fact, if an object is involved in a nondeterministic computation then it is necessary to restore the status it had before the last choice point whenever the computation backtracks and tries a different alternative. In JSetL this is obtained by allowing the solver to auto- matically save and restore the global status of all logical objects involved in the computation. Since a logical object is characterized by the fact that its value, if any, can not be changed through side-effects, saving and restoring the status of logical objects is a relatively simple task for the solver. Hence, we will always use logical objects, in particular logical variables, for all those objects that are involved in nondeterministic computations. As a consequence of this choice we can not manipulate logical objects by using the usual imperative statements (e.g., the assignment), but we will always need to use constraints to deal with them. In particular, the equality constraint l.eq(v) is used to unify a logical variable l with a value v. If l is unbound, this simply amounts to binding l to v. Once assigned, however, the value v is no longer modifiable. As an example let us consider the implementation of the nondeterministic function split(l) shown in Example 2. This function can be implemented in JSetL by a user-defined constraint split(l,l1,l2). Note that, here we are replacing a function with the corresponding relation: in fact, split(l,l1,l2) defines a ternary relation whose elements are those triples hl,l1,l2i, with l, l1 and l2 belonging to the domain of lists, such that all elements of l are split into two lists l1 and l2. As noted above, variables dealt with by nondeterministic constraints are conveniently represented in JSetL as logical objects. Thus, we represent the lists l, l1, and l2 of the constraint split as JSetL’s logical lists (i.e., objects of the class LList) and we manipulate them through constraints over lists. 2 Note that the constructor of the super class NewConstraintsClass, which is invoked when myOps is created, provides for storing (a reference to) itself within the specified solver, so making the latter able to invoke the method user code of the created object myOps. 220 Gianfranco Rossi and Federico Bergenti Example 4. (split in JSetL) Define a constraint split(l,l1,l2) which is true if all elements of the list l are split into two lists l1 and l2. public Constraint split(LList l,LList l1,LList l2) { return new Constraint("split",l,l1,l2); } private void split(Constraint c) throws Failure { LList l = (LList)c.getArg(1); LList l1 = (LList)c.getArg(2); LList l2 = (LList)c.getArg(3); switch(c.getAlternative()) { case 0: solver.addChoicePoint(c); solver.add(l.eq(LList.empty())); // l is [] solver.add(l1.eq(LList.empty())); // l1 is [] solver.add(l2.eq(LList.empty())); // l2 is [] break; case 1: { solver.addChoicePoint(c); LVar x = new LVar(); LList r = new LList(); LList r1 = new LList(); solver.add(l.eq(r.ins(x))); // 1st element (x) and rest (r) of l solver.add(split(r,r1,l2)); // split r into two lists r1 and l2 solver.add(l1.eq(r1.ins(x))); // l1 is [x|r1] break; } case 2: { LVar x = new LVar(); LList r = new LList(); LList r2 = new LList(); solver.add(l.eq(r.ins(x))); // 1st element (x) and rest (r) of l solver.add(split(r,l1,r2)); // split r into two lists l1 and r2 solver.add(l2.eq(r2.ins(x))); // l2 is [x|r2] break; } } } split implements the nondeterministic construct orelse by using the meth- ods getAlternative and addChoicePoint. The invocation c.getAlternative() returns an integer associated with the constraint c that can be used to count the nondeterministic alternatives within this constraint. Its initial value is 0. Each time the constraint c is re-considered due to backtracking, the value re- turned by c.getAlternative() is automatically incremented by 1. The invo- cation solver.addChoicePoint(c) adds a choice point to the alternative stack of the current solver. This allows the solver to backtrack and re-consider the constraint c if a failure occurs subsequently. Note that the JSetL implementation of the method split closely resembles the abstract definition in pseudo-code of the function split given in Section 3. In particular, lists are implemented as LList objects, and the addition and extrac- tion of elements from such lists is performed through the JSetL constraint eq. Nondeterministic Programming in Java with JSetL 221 Specifically, l.eq(r.ins(x)) is true if l is the list composed by an element x and a remaining part r. If l is known and x and r are not, solving l.eq(r.ins(x)) amounts to computing the first element x and the rest r of the list l, whereas if x and r are known and l is not, l.eq(r.ins(x)) can be used to build the list l out of its parts x and r. Finally, note that the recursive call to split(r) in the abstract definition of the function split is replaced by the (recursive) posting of the constraint split(r,r1,l2) (or split(r,l1,r2)) in the above concrete implementation. As a sample use of split, if l is the LList with value ["I","you","she","we", "you","they"], sumLength(l,n) is a user-defined (deterministic) constraint that implements the function sumLength(l) of Example 2, listOps is an in- stance of the class that extends NewConstraintsClass containing split and sumLength, and l1, l2 are unbound logical lists and n, m are unbound logical variables, then executing the following fragment of code solver.add(listOps.split(l,l1,l2)); solver.add(listOps.sumLength(l1,n)); solver.add(listOps.sumLength(l2,m)); solver.check(m.eq(n)); will bind l1 to ["you","I","they"], l2 to ["she","you","we"], and n and m to 8. Remark 1. The use of relations in place of functions, along with the use in their implementation of a number of specific features provided by JSetL, have another important consequence on the usability of user-defined constraint methods. Let us consider a function y = f (x) and a possible call to it, z = f (a). In JSetL one can define a constraint cf (x, y) which represents the relation Rf = {hx, yi : y = f (x)} and then solve the constraint cf (a, z). Solving this constraint actually amounts to checking whether ha, zi ∈ Rf , for some z. While calling f (x) to compute y implies assuming x to be the input parameter and y the output, solving cf (x, y) does not make any assumption on the “direction” of their parameters. Thus, one can compute y out of a given x, or, vice versa, x out of a given y, or one can test whether the relation among two given values x and y holds, or one can compute any of the pairs hx, yi belonging to Rf . Hence, the same method can be used to implement different, though related, functions.3 This general use of user-defined constraints in JSetL is made possible thanks to the availability of a number of different facilities to be used in the constraint implementation. Specifically, – the use of logical variables as parameters – the use of unification in place of equality and assignment – the use of nondeterminism to compute multiple solutions for the same con- straint. 3 It worth emphasizing here the similarity with Prolog or, more to the point, with Prolog-based CP languages. 222 Gianfranco Rossi and Federico Bergenti Note that the fact that a logical variable acts as an input or as an output parameter depends on the fact it is bound or not when the method is called. In particular, unbound variables can be easily used to obtain output parameters. Moreover, if the value bound to a variable is a partially specified aggregate, e.g. a logical list, then it can act simultaneously as input and as output, i.e. as an input-output parameter. For example, let us consider the fragment of code shown at the end of Example 4. If we want to say, for instance, that l2 must contain two repeated elements, then the above statements can be preceded by the following declarations LVar x = new LVar(); LList l2 = new LList().ins(x).ins(x); In this way split is called with its third argument bound to the partially spec- ified list [x,x| ] instead of being left unbound. Thus, solving split(l,l1,l2) will bind l1 to ["I","she","they"] and l2 to ["you","you","we"]. 5 Implementing DCGs In this section we show a more complete example of application of the facilities offered by JSetL to support nondeterminism: the implementation of Definite Clause Grammars [8]. A Definite Clause Grammar (DCG) is a way to represent a context-free grammar as a set of first-order logic formulae in the form of definite clauses. As such, DCGs are closely related to Logic Programming, and tools for dealing with DCGs are usually provided by current Prolog systems. Given the DCG repre- sentation of a grammar one can immediately obtain a parser for the language it describes by viewing the DCG as a set of Prolog clauses and using the Prolog interpreter to execute them. In this section we show how DCGs can be conveniently used also in the context of more conventional languages, such as Java, provided the language is equipped with a few features that are fundamental to support DCGs processing, namely (logical) lists and nondeterminism. We prove this claim by showing how DCGs can be encoded and processed using Java with JSetL. Consider the following excerpt of a grammar of constant arithmetic expres- sions hexpri ::= hnumi|hnumi + hexpri|hnumi − hexpri Assume that input to be parsed is represented as a list of numerals and symbols. For example, [8, +, 2, -, 7] is a valid hexpri. This grammar may be encoded in terms of first-order logic formulae in clausal form in the following way: create one predicate for each non-terminal in the grammar and define each predicate using one clause for each alternative form of the corresponding non-terminal. Each predicate takes two arguments, the first being the list representation of the input stream, and the second being instantiated to the list of input elements that remain after a complete syntactic Nondeterministic Programming in Java with JSetL 223 structure has been found. As an example, the above grammar can be written as a DCG as follows (using a pure Prolog notation). Example 5. (A DCG for hexpri) expr(L, Remain) :- num(L, Remain). expr(L, Remain) :- num(L, L1), L1 = [+|L2], expr(L2, Remain). expr(L, Remain) :- num(L, L1), L1 = [-|L2], expr(L2, Remain). num(L, Remain) :- L = [D|Remain], number(D). where the predicate number(D) is true if D is a numeric constant.4 This grammar representation constitutes an executable Prolog program that can be immediately used as a top-down parser for the denoted language. Using this program we can prove that, for example, expr([1, +, 2, -, 3], []) is true (i.e., 1+2-3 is a valid arithmetic expression), while expr([1, +, 2, -], []) is false. The DCG shown in Example 5, that we have written as a Prolog program, can be implemented with a relatively little effort as a JSetL program as well. Each predicate corresponding to a non-terminal in the grammar is implemented as a new JSetL constraint, that is a method of a class extending the class NewConstraintsClass. These methods exploit the nondeterministic features provided by JSetL to support the nondeterministic choice from among differ- ent clauses for the same predicate. List data structures are implemented using JSetL logical lists, that is objects of the class LList. In particular, partially specified lists with an unknown rest (i.e., [o | l], l unbound) can be constructed using the method ins and accessed through unification. The complete JSetL implementation of the DCG shown above is given in Example 6. Example 6. (Implementing the DCG for hexpri in JSetL) public class ExprParser extends NewConstraintsClass { public ExprParser(SolverClass currentSolver) { super(currentSolver); } public Constraint expr(LList L, LList Remain) { return new Constraint("expr", L, Remain); } 4 Special syntax exists in current Prolog systems that allows EBNF-like specification of DCGs. For instance, the second clause of Example 5 can be written in Prolog alternatively as expr --> num, [+], expr. The Prolog interpreter automatically translates this special form to the pure clausal form used in Example 5. 224 Gianfranco Rossi and Federico Bergenti public Constraint num(LList L, LList Remain) { return new Constraint("num", L, Remain); } public Constraint number(LVar n) { return new Constraint("number", n); } protected void user_code(Constraint c) throws NotDefConstraintException { if (c.getName().equals("expr")) expr(c); else if (c.getName().equals("num")) num(c); else if (c.getName().equals("number")) number(c); else { throw new NotDefConstraintException(); } } private void expr(Constraint c) { LList L = (LList)c.getArg(1); LList Remain = (LList)c.getArg(2); switch (c.getAlternative()) { // expr(L, Remain) :- num(L, Remain). case 0: { solver.addChoicePoint(c); solver.add(num(L, Remain)); break; } // expr(L, Remain) :- num(L, L1), L1 = [+|L2], expr(L2, Remain). case 1: { solver.addChoicePoint(c); LList L1 = new LList(); LList L2 = new LList(); solver.add(num(L, L1)); solver.add(L1.eq(L2.ins(’+’))); solver.add(expr(L2, Remain)); break; } // expr(L, Remain) :- num(L, L1), L1 = [-|L2], expr(L2, Remain). case 2: { LList L1 = new LList(); LList L2 = new LList(); solver.add(num(L, L1)); solver.add(L1.eq(L2.ins(’-’))); solver.add(expr(L2, Remain)); } } } Nondeterministic Programming in Java with JSetL 225 private void num(Constraint c) { LList L = (LList)c.getArg(1); LList Remain = (LList)c.getArg(2); LVar D = new LVar(); solver.add(L.eq(Remain.ins(D))); solver.add(number(D)); } private void number(Constraint c) { LVar n = (LVar)c.getArg(1); if (n.getValue() instanceof Integer) return; else c.fail(); } } If, for example, the expression to be parsed is 5 + 3 − 2, which is represented by a logical list tokenList with value [’5’,’+’,’3’,’-’,’2’], and sampleParser is an instance of the class ExprParser, then the invocation solver.check(sampleParser.expr(tokenList,LList.empty())) will return true, while, if tokenList has value [’5’,’+’,’3’,’-’], the same invo- cation to sampleParser.expr will return false. Actions to be performed when a non-terminal has been successfully reduced (e.g., in order to evaluate the parsed expression or to generate the correspond- ing target code) can be easily added to a DCG by adding new arguments to predicates defining non-terminals and new atoms at the end of the body of the corresponding clauses. Accordingly, the JSetL implementation of a DCG can be easily extended by adding new arguments and suitable statements to the user-defined constraints implementing the non-terminals. 6 Conclusions and future work In this paper we have made evident, through a number of simple examples, that nondeterministic programming is conveniently exploitable also within conven- tional O-O languages such as Java. We have obtained this by combining a num- ber of different features offered by the Java library JSetL: set data abstractions, nondeterministic constraint solving, logical variables, unification, user-defined constraints. In particular, general nondeterministic procedures can be defined in JSetL as new user-defined constraints, taking advantage of the facilities for expressing and handling nondeterminism provided by the solver. The JSetL library, along with the source code of all the sample programs shown in this paper, can be downloaded from the JSetL’s home page at http: //cmt.math.unipr.it/jsetl.html. 226 Gianfranco Rossi and Federico Bergenti As a future work we plan to identify the minimal extensions to be made to the JSetL’s solver to make it capable of supporting, with the same approach outlined in this paper, other nondeterministic control structures e.g. the ones described in [1] and [12]. Acknowledgments This work has been partially supported by the G.N.C.S. project “Specifica e verifica di algoritmi tramite strumenti basati sulla teoria degli insiemi”. Special thanks to Luca Chiarabini who took part and stimulated the preliminary discus- sions on this work, and to Andrea Longo who contributed to the development of the JSetL based implementation of DCGs. References 1. K.R. Apt, J. Brunekreef, V. Partington, A. Schaerf (1998) Alma-0: An Imperative Language that Supports Declarative Programming. ACM Transactions on Programming Languages and Systems (TOPLAS), Vol. 20, No. 5, 1014–1066. 2. K.R. Apt, A. Schaerf (1999) The Alma Project, or How First-Order Logic Can Help Us in Imperative Programming. In E.-R. Olderog, B. Steffen, Eds., Correct System Design, LNCS, Springer-Verlag, v. 1710, 89–113. 3. F. Bergenti, L. Chiarabini, and G. Rossi (2011) Programming with Partially Specified Aggregates in Java. Computer Languages, Systems & Structures, Elsevier, 37(4), 178–192. 4. J. Cohen (1979) Non-Deterministic Algorithms. Computing Surveys, Vol. 11, No. 2, 79–94. 5. A. Dovier, C. Piazza, E. Pontelli, and G. Rossi (2000) Sets and Constraint Logic Programming. ACM Transactions on Programming Languages and Systems, 22(5):861–931. 6. A. Dovier, E. Pontelli, and G. Rossi (2006) Set unification. Theory and Practice of Logic Programming, 6:645–701. 7. S.H. Mirian-HosseinaAbadi, M.R. Mousavi (2002) Making Nondeterminism Explicit in Z. Proceedings of the Iranian Computer Society Annual Conference (CSICC 02), Tehran, Iran. 8. F.C.N. Pereira, D.H.D. Warren (1980) Definite clause grammars for language analysis - A survey of the formalism and a comparison with augmented transition networks. Artificial Intelligence 13, 3, 231-278. 9. G. Rossi, E. Panegai, and E. Poleo (2007) JSetL: A Java Library for Support- ing Declarative Programming in Java. Software-Practice & Experience, 37:115-149. 10. J. T. Schwartz, R. B. K. Dewar, E. Dubinsky, and E. Schonberg (1986) Programming with sets, an introduction to SETL. Springer-Verlag. 11. H. Sondergaard, P. Sestoft (1992) Non-Determinism in Functional Languages. The Computer Journal, 35(5), 514-523. 12. P. Van Hentenryck, L. Michel (2006) Nondeterministic Control for Hybrid Search. Constraints, Vol. 11, No. 4, 353– 373. 13. M. Walicki, S. Meldal (1993) Sets and Nondeterminism. Presented at ICLP’93 Post-Conference Workshop on Logic Programming with Sets (http://people. math.unipr.it/gianfranco.rossi/sets/body_workshop.html), Budapest.