1 Introduction
Functional logic languages, such as Curry and TOY, combine the most important features of functional and logic languages. In particular, the combination of lazy evaluation and nondeterminism leads to better evaluation strategies compared to logic programming [2] and new design patterns [3]. However, the combination of these features poses new challenges. In particular, the encapsulation of nonstrict nondeterministic computations has not a universally accepted solution so that different Curry systems offer different implementations for it. Encapsulating nondeterministic computations is an important feature for application programming when the task is to show whether some problem has a solution or to compare different solutions in order to compute the best one. For this purpose, let denote the set of all the values of an expression . The problem with such an encapsulation operator is the fact that the might share subexpressions which are defined outside . For instance, consider the expression
let x = 0?1 in (1)
The infix operator “?” denotes a nondeterministic choice, i.e., the expression “0?1” has two values: 0 or 1. Since the nondeterminism of x is introduced outside , the question arises whether this should be encapsulated. Strong encapsulation, which is similar to Prolog’s findall, requires to encapsulate all nondeterminism occurring during the evaluation of the encapsulated expression. In this case, expression (1) evaluates to the set . As discussed in [8], a disadvantage of strong encapsulation is its dependence on the evaluation strategy. For instance, consider the expression
let x = 0?1 in (, x) (2)
If the tuple is evaluated from left to right, the first component evaluates to but the second component nondeterministically evaluates to the values 0 and 1 so that the expression (1) evaluates to the values (,0) and (,1). However, in a righttoleft evaluation of the tuple, x is evaluated first to one of the values 0 and 1 so that, due to sharing, the expression (1) evaluates to the values (,0) and (,1).
To avoid this dependency on the evaluation strategy, weak encapsulation of only encapsulates the nondeterminism of but not the nondeterminism originating from expressions created outside . Thus, weak encapsulation produces the last result of (1) independent of the evaluation strategy. Weak encapsulation has the disadvantage that its meaning depends on the syntactic structure of expressions. For instance, the expressions “let x = 0?1 in ” and “” have different values. To avoid misunderstandings and make the syntactic structure of encapsulation explicit, set functions have been proposed [5]. For any function , there is a set function which computes the set of all the values for given argument values. The set function encapsulates the nondeterminism caused by the definition of but not nondeterminism originating from arguments. For instance, consider the operation
double x = x + x (3)
The result of double is always a set with a single element since the definition of double does not contain any nondeterminism. Thus, double (0?1) evaluates to the two sets and .
Although set functions fit well into the framework of functional logic programming, their implementation is challenging. For instance, the Curry system PAKCS [15] compiles Curry programs into Prolog programs so that nondeterminism is implemented for free. Set functions are implemented in PAKCS by Prolog’s findall. To obtain the correct separation of nondeterminism caused by arguments and functions, as discussed above, arguments are completely evaluated before the findall encapsulation is invoked. Although this works in many cases, there are some situations where this implementation does not deliver any result. For instance, if the complete evaluation of arguments fails or does not terminate, no result is computed even if the set function does not demand the complete argument values. Furthermore, if the set is infinite, findall does not terminate even if the goal is only testing whether the set is empty. Thus, the PAKCS implementation of set functions is “too strict.”
These problems are avoided by the implementation of set functions in the Curry system KiCS2, which compiles Curry programs into Haskell programs and represents nondeterministic values in treelike structures [9]. A similar but slightly different representation is used to implement set functions. Due to the interaction of different levels of nondeterminism, the detailed implementation is quite complex so that a simpler implementation is desirable.
In this paper, we propose a new implementation of set functions that can be added to any Curry system. It avoids the disadvantages of existing implementations by synthesizing an explicit definition of a set function for a given function. Depending on the source code of a function, simple or more complex definitions of a set function are derived. For instance, nested set functions require a more complex scheme than toplevel set functions, and functions with nonlinear righthand sides require an explicit implementation of the calltime choice semantics.
The paper is structured as follows. In the next section, we review some aspects of functional logic programming and Curry. After the definition of set functions in Sect. 3, we introduce in Sect. 4 plural functions as an intermediate step towards the synthesis of set functions. A first and simple approach to synthesize set functions is presented in Sect. 5 before we discuss in Sect. 6 and 7 the extension to nonlinear rules and nested set functions, respectively. Sect. 8 discussed related work before we conclude in Sect. 9.
2 Functional Logic Programming and Curry
We assume familiarity with the basic concepts of functional logic programming [6, 14] and Curry [16]. Therefore, we briefly review only those aspects that are relevant for this paper.
Although Curry has a syntax close to Haskell [21], there is an important difference in the interpretation of rules defining an operation. If there are different rules that might be applicable to reduce an expression, all rules are applied in a nondeterministic manner. Hence, operations might yield more than one result on a given input. Nondeterministic operations, which are interpreted as mappings from values into sets of values [13], are an important feature of contemporary functional logic languages. The archetype of nondeterministic operations is the choice operator “?” defined by
Typically, this operator is used to define other nondeterministic operations like
Thus, the expression coin evaluates to one of the values 0 or 1. Nondeterministic operations are quite expressive since they can be used to completely eliminate logic variables in functional logic programs, as show in [4, 11]. Therefore, we ignore them in the formal development below. For instance, a Boolean logic variable can be replaced by the nondeterministic generator operation for Booleans defined by
aBool = False ? True (4)
Passing nondeterministic operations as arguments, as in the expression doublecoin, might cause a semantical ambiguity. If the argument coin is evaluated before calling double, the expression has two values, 0 and 2. However, if the argument coin is passed unevaluated to the righthand side of double and, thus, duplicated, the expression has three different values: 0, 1, or 2. These two interpretations are called calltime choice and runtime choice [18]. Contemporary functional logic languages stick to the calltime choice, since this leads to results which are independent of the evaluation strategy and has the rewriting logic CRWL [13] as a logical foundation for declarative programming with nonstrict and nondeterministic operations. Furthermore, it can be implemented by sharing which is already available in implementations of nonstrict languages. In this paper, we use a simple reduction relation, equivalent to CRWL, that we sketch without giving all details (which can be found in [19]).
A value is an expression without defined operations.
To cover nonstrict computations, expressions can also
contain the special symbol to represent
undefined or unevaluated values.
A partial value is a value that might contain occurrences of .
A partial constructor substitution is a substitution
that replaces variables by partial values.
A context is an expression with some “hole.”
Then expressions are reduced according to the following
reduction relation:
where is a program rule
and a partial constructor substitution
where
The first rule models the calltime choice: if a rule is applied,
the actual arguments of the operation must have been evaluated to
partial values. The second rule models nonstrictness by allowing
the evaluation of any subexpression to an undefined value
(which is intended if the value of this subexpression is not demanded).
As usual, denotes the reflexive and transitive closure
of this reduction relation.
We also write instead of if is a (partial) value.
For the sake of simplicity, we assume that programs are already translated into a simple standard form: conditional rules are replaced by ifthenelse expressions and the lefthand sides of all operations (except for “?”) are uniform [20] i.e., either the operation is defined by a single rule where all arguments are distinct variables, or in the lefthand sides of all rules only the last (or any other fixed) argument is a constructor with variables as arguments where each constructor of a data type occurs in exactly one rule. In particular, rules are not overlapping so that nondeterminism is represented by ?expressions.
3 Set Functions
Set functions have been introduced in Sect. 1. Their main feature is to encapsulate only the nondeterminism caused by the definition of the corresponding function. Similarly to nondeterminism, set functions encapsulate failures only if they are caused by the function’s definition. If failed denotes a failing computation, i.e., an expression without a value, the expression doublefailed has no value (and not the empty set as a value). Since the meaning of failures and nested set functions has not been discussed in [5], Christiansen et al. [10] propose a rigorous denotational semantics for set functions. In order to handle failures and choices in nested applications of set functions, computations are attached with “nesting levels” so that failures caused by different encapsulation levels can be distinguished.
In the following, we use a simpler model of set functions. Formally, set functions return sets. However, for simplicity, our implementation returns multisets, instead of sets, represented as some abstract data type. Converting multisets into sets is straightforward for the representation we choose. If is a type, denotes the type of a set of elements of type . The meaning of a set function can be defined as follows:
Definition 1
Given a unary (for simplicity) function , the set function of , , is defined as follows: For every partial value of type , value of type , and set of elements of type , iff and .
This definition accommodates the different aspects of set functions discussed in the introduction. If the evaluation of an expression leads to a failure or choice but its value is not required by the set function, it does not influence the result of the set function since can be derived to the partial value .
Example 1
Consider the following function:
ndconst x y = x ? 1 (5)
The value of ndconst2failed is , and ndconst(2?4)failed has the values and .
Given a function , we want to develop a method to synthesize the definition . A difficulty is that might be composed of other functions whose nondeterminism should also be encapsulated by . This can be solved by introducing plural functions, which are described next.
4 Plural Functions
If and are functions, their composition is well defined by for all of type . However, the corresponding set functions, and , are not composable because their types mismatch—an output of cannot be an input of . To support the composition of functions that return sets, we need functions that take sets as arguments.^{1}^{1}1The notion of “plural function” is also used in [22] to define a “plural” semantics for functional logic programs. Although the type of their plural functions is identical to ours, their semantics is quite different.
Definition 2
Let be a function. We call plural function of any function with the following property: for all and such that , (1) if then there exists some such that and (2) if and , then .
The above definition generalizes to functions with more than one argument. The following display shows both the type and an example of application of the plural function, denoted by “++”, of the usual list concatenation “++”:
(++) :: {[a]} > {[a]} > {[a]}
{[1],[2]} ++ {[],[3]} = {[1],[1,3],[2],[2,3]}
(6)
Plural functions are unique, composable, and cover all the results of set functions (see appendix for details). Since plural functions are an important step towards the synthesis of set functions, we discuss their synthesis first. To implement plural functions in Curry, we have to decide how to represent sets (our implementation returns multisets) of elements. An obvious representation are lists. Since we will later consider nonlinear rules and also nested set functions where nondeterminism of different encapsulation levels are combined, we use search trees [8] to represent choices between values. The type of a search tree parameterized over the type of elements can be defined as follows:
Hence, a search tree is either an expression in headnormal form, a failure, or a choice between search trees. Although this definition does not enforce that the argument of a Val constructor is in headnormal form, this invariant will be ensured by our synthesis method for set functions, as presented below. For instance, the plural function of the operation aBool (2) can be defined as
The plural function of the logical negation not defined by
not False = True
not True = False
(7)
takes a search tree as an argument so that its definition must match all search tree constructors. Since the matching structure is similar for all operations performing pattern matching on an argument, we use the following generic operation to apply an operation defined by pattern matching to a nondeterministic argument:
^{2}^{2}2Actually, this operation is the monadic “bind” operation with flipped arguments if ST is an instance of MonadPlus, as proposed in [12]. Here, we prefer to provide a more direct implementation.Hence, failures remain as failures, and a choice in the argument leads to a choice in the result of the operation, which is also called a pulltab step [1]. Now the plural function of not can be defined by (shortly we will specify a systematic translation method)
The synthesis of plural functions for uniform programs is straightforward: pattern matching is implemented with applyST and function composition in righthand sides comes for free. For instance, the plural function of
is
So far we considered only base values in search trees. If one wants to deal with structured data, like lists of integers, a representation like ST [Int] is not appropriate since nondeterminism can occur in any constructor of the list, as shown by
The expression one23 evaluates to [1], [2], [1,3], and [2,3]. If we select only the head of the list, the nondeterminism in the tail does not show up, i.e., headone23 evaluates to two values 1 and 2. This demands for a representation of headnormal forms with possible search tree arguments. The headnormal forms of nondeterministic lists are the usual list constructors where the cons arguments are search trees:
The plural representation of one23 is
The plural function of head is synthesized as
so that headPone23P evaluates to Choice(Val1) (Val2), as intended.
To provide a precise definition of this transformation, we assume that all operations in the program are uniform (see Sect. 2). The plural transformation of these kinds of function definitions is defined as follows (where denotes the constructor of the nondeterministic type, like STList, corresponding to the original constructor ):
Note that and have different types in the original and transformed program, e.g., an argument of type Int is transformed into an argument of type STInt. Furthermore, expressions occurring in the function bodies are transformed according to the following rules:
Fail 
The presented synthesis of plural functions is simple and yields compositionality and laziness. Thus, they are a good basis to define set functions, as shown next.
5 Synthesis of Set Functions: The Simple Way
Plural functions take sets as arguments whereas set functions are applied to standard expressions which might not be evaluated. To distinguish these possibly unevaluated arguments from headnormal forms, we add a new constructor to search trees
and extend the definition of applyST with the rule
Furthermore, plural functions yield nondeterministic structures which might not be completely evaluated. By contrast, set functions yield sets of values, i.e., completely evaluated elements. In order to turn a plural function into a set function, we have to evaluate the search tree structure into the set of their values. For the sake of simplicity, we represent the latter as ordinary lists. Thus, we need an operation like
to extract all the values from a search tree. For instance, the expression
should evaluate to the list [1,2]. This demands for the evaluation of all the values in a search tree (which might be headnormal forms with choices at argument positions) into its complete normal form. We define a type class^{3}^{3}3Although the current definition of Curry [16] does not include type classes, many implementations of Curry, like PAKCS, KiCS2, or MCC, support them. for this purpose:
Each instance of this type class must define a method nf which evaluates a given headnormal form into a search tree where all Val arguments are completely evaluated. Instances for base types are easily defined:
The operation nf is easily extended to arbitrary search trees:^{4}^{4}4The use of seq ensures that the Uneval argument is evaluated. Thus, nondeterminism and failures in arguments of set functions are not encapsulated, as intended.
Now we can define an operation that collects all the values in a search tree (without Uneval constructors) into a list by a depthfirst strategy:
Thus, failures are ignored and choices are concatenated. Combining these two operations yields the desired definition of stValues:
NF instances for structured types can be defined by moving choices and failures in arguments to the root:
For instance, the nondeterministic list value [1?2] can be described by the ST structure
so that stValuesnd01 moves the inner choice to the toplevel and yields the list
which represents the set .
As an example for our first approach to synthesize set functions, consider the following operation (from Curry’s prelude) which nondeterministically returns any element of a list:
Since set functions do not encapsulate nondeterminism caused by arguments, the expression anyOf[0?1,2,3] evaluates to the sets and .
In order to synthesize the set function for anyOf by exploiting plural functions, we have to convert ordinary types, like [Int], into search tree types, like ST (STList Int), and vice versa. For this purpose, we define two conversion operations for each type and collect their general form in the following type class:^{5}^{5}5Multiparameter type classes are not yet supported in the Curry systems PAKCS and KiCS2. The code presented here is more elegant, but equivalent, to the actual implementation.
Instances for base and list types are easily defined:
where the operation toST is like toValST but adds a Uneval constructor:
The (informal) precondition of fromValST is that its argument is already fully evaluated, e.g., by an operation like stValues. Therefore, we define the following operation to translate an arbitrary search tree into the list of its Curry values:
As already mentioned, we use lists to represent multisets of values:
However, one could also use another (abstract) data type to represent multisets or even convert them into sets, if desired.
Now we have all parts to synthesize a set function: convert an ordinary value into its search tree representation, apply the plural function on it, and translate the search tree back into the multiset (list) of the values contained in this tree. We demonstrate this by synthesizing the set function of anyOf.
The uniform representation of anyOf performs complete pattern matching on all constructors:
We easily synthesize its plural function according to the scheme of Sect. 4:
Finally, we obtain its set function by converting the argument into the search tree and the result of the plural function into a multiset of integers:
The behavior of our synthesized set function is identical to their original definition, e.g., anyOfS[0?1,2,3] evaluates to the lists [0,2,3] and [1,2,3], i.e., nondeterminism caused by arguments is not encapsulated. This is due to the fact that the evaluation of arguments, if they are demanded inside the set function, are initiated by standard pattern matching so that a potential nondeterministic evaluation leads to a nondeterministic evaluation of the synthesized set function.
In contrast to the strict evaluation of set functions in PAKCS, as discussed in the introduction, our synthesized set functions evaluate their arguments lazily. For instance, the set function of ndconst defined in Example 1 is synthesized as follows:
Since the second argument of ndconstS is never evaluated, the expression ndconstS2failed evaluates to [2,1] and ndconstS(2?4)(3?5) yields the lists [2,1] and [4,1]. The set function implementation of PAKCS fails on the first expression and yields four results on the second one. Hence, our synthesized set function yields better results than PAKCS, in the sense that it is more complete and avoids duplicated results. Morever, specific primitive operations, like findall, are not required.
The latter property is also interesting from another point of view. Since PAKCS uses Prolog’s findall, the evaluation strategy is fixed to a depthfirst search strategy implemented by backtracking. Our implementation allows more flexible search strategies by modifying the implementation of stValues. Actually, one can generalize search trees and stValues to a monadic structure, as done in [7, 12], to implement various strategies for nondeterministic programming.
A weak point of our current synthesis is the handling of failures. For instance, the evaluation of anyOfS[failed,1] fails (due to the evaluation of the first list element) whereas according to Def. 1. To correct this incompleteness, failures resulting from argument evaluations must be combined with result sets. This can be done by extending search trees and distinguishing different sources of failures, but we omit it here since a comprehensive solution to this issue will be presented in Sect. 7 when nested applications of set functions are discussed.
6 Adding CallTime Choice
We have seen in Sect. 1 that the expression double(0?1) should evaluate to the values 0 or 2 due to the calltime choice semantics. Thus, the set function of
should yield the multiset . However, with the current synthesis, the corresponding set function yields the list [0,1,1,2] and, thus, implements the runtime choice. The problem arises from the fact that the nondeterminstic choice in the synthesized plural function
is duplicated by doubleP. In order to implement the calltime choice, the same decision (left or right choice) for both duplicates has to be made. Instead, the search operation searchDFS handles these choices independently and is unaware of the duplication.
To tackle this problem, we follow the idea implemented in KiCS2 [9] and extend our search tree structure by identifiers for choices (represented by the type ID) as follows:
The changes to previously introduced operations on search trees, like applyST or nfST, are minimal and straightforward as we only have to keep a choice’s identifier in their definitions. The most significant change occurs in the search operation. As shown in [9], the calltime choice can be implemented by storing the decision for a choice, when it is made for the first time, during the traversal of the search tree and looking it up later when encountering the same choice again. We introduce the type
for decisions and use an association list^{6}^{6}6Of course, one can replace such lists by more efficient access structures. as an additional argument to the search operation to store such decisions. The adjusted depthfirst search then looks as follows:
When extracting all the values from a search tree, we initially pass an empty list to the search operation since no decisions have been made at that point:
Finally, we have to ensure that the choices occurring in synthesized plural functions are provided with unique identifiers. To this end, we assume a type IDSupply that represents an infinite set of such identifiers along with the following operations:
The operation initSupply yields an initial identifier set. The operation uniqueID yields an identifier from such a set while the operations leftSupply and rightSupply both yield disjoint subsets without the identifier obtained by uniqueID (see [9] for a discussion about implementing these operations.). When synthesizing plural functions, we add an additional argument of type IDSupply and use the aforementioned operations on it to provide unique identifiers to every choice. The synthesized set function has to pass the initial identifier supply initSupply to the plural function. In the case of double01, it looks as follows:
With this modified synthesis, the set function yields the expected result [0,2]. Note that this extended scheme is necessary only if some operation involved in the definition of the set function has rules with nonlinear righthand sides, i.e., might duplicate argument expressions. For the sake of readability, we omit this extension in the next section where we present another extension necessary when set functions are nested.
7 Synthesis of Nested Set Functions
So far we considered the synthesis of set functions that occur only at the toplevel of functional computations, i.e., which are not nested inside other set functions. The synthesis was based on the translation of functions involved in the definition of a set function into plural functions and extracting all the values represented by a search tree into a list structure. If set functions are nested, the situation becomes more complicated since one has to define the plural function of an inner set function. Moreover, choices and failures produced by different set functions, i.e., levels of encapsulations, must be distinguished according to [10]. Although nested set functions are seldom used, a complete implementation of set functions must consider them. Therefore, we discuss in this section how we can extend the scheme proposed so far to accommodate nested set functions.
The original proposal of set functions [5] emphasized the idea to distinguish nondeterminism of arguments from nondeterminism of the function definition. However, the influence of failing computations and the combination of nested set functions was not specified. These aspects are discussed in [10] where a denotational semantics for functional logic programs with weak encapsulation is proposed. Roughly speaking, an encapsulation level is attached to failures and choices. These levels are taken into account when value sets are extracted from a nested nondeterminism structure to ensure that failures and choices are encapsulated by the function they belong to and not any other. We can model this semantics by extending the structure of search trees as follows:
The additional argument of the constructors Fail and Choice specifies the encapsulation level.
Consider the definition
notf = failed (8)
and the expression notf. Although the righthand side of notf fails because the argument of not is demanded w.r.t. the definition (4), the source of the failure is inside its definition so that the failure is encapsulated and the result of notf is the empty set. However, if we define
nots x = x (9)
and evaluate notsfailed, the computation fails since the failure comes from outside and is not encapsulated. These issues are discussed in [10] where it has been argued that failures outside encapsulated search should lead to a failure instead of an empty set only if there are no other results. For instance, the expression anyOffailed has no value (since the demanded argument is an outside failure) whereas the value of the expression
anyOf[failed,1] (10)
is the set with the single element 1. This semantics can be implemented by comparing the levels of failures occurring in search trees (see [10] for details).
With the extension of search trees introduced above, we are well prepared to implement this semantics in Curry itself except for one point: outside failures always lead to a failure of the complete evaluation if their value is needed in the encapsulated search. Thus, the evaluation of (7) will always fail. In order to avoid this, we have to transform such a failure into the search tree element Fail0 (where 0 is the “top” encapsulation level, i.e., outside any set function). For this purpose, we modify the definitions of applyST and nfST on arguments matching the Uneval constructor by checking whether the evaluation of the argument to a headnormal form fails:^{7}^{7}7This requires a specific primitive isFail to catch failures, which is usually supported in Curry implementations to handle exceptions.
Then one can synthesize plural and set functions similarly to the already presented scheme. In order to set the correct encapsulation level in Fail and Choice constructors, every function has the current encapsulation level as an additional argument. Finally, one also has to synthesize plural functions of set functions if they are used inside other set functions. For instance, the set function of not has type
but the plural function of this set function must represent the result set again as a search tree, i.e., it has the type
(the first argument is the encapsulation level). To evaluate the search tree structure returned by such plural set functions, we need an operation which behaves similarly to stValues but returns a search tree representation of the list of values, i.e., this operation has the type
Note that this operation also takes the encapsulation level as its first argument. For instance, failures are only encapsulated (into an empty list) if they are on the same level, i.e., there is the following defining rule for stValuesP:
Choices are treated in a similar way where failures in different alternatives are merged to their maximum level according to the semantics of [10], e.g.,
evaluates to Val Nil (representing the empty set of values).
Now we can define notSP by evaluating the result of notP with stValuesP where the current encapsulation level is increased:
The plural function of notf (7) is straightforward (note that the level of the generated failure is the current encapsulation level):
The set function of notf is synthesized as presented before except that we additionally provide 1 as the initial encapsulation level (this is also the level encapsulated by fromST):
As we have seen, nested set functions can be synthesized with a scheme similar to simple set functions. In order to correctly model the semantics of [10], an encapsulation level is added to each translated operation which is used to generate the correct Fail and Choice constructors. In order integrate the synthesized set functions into standard Curry programs, arguments passed to synthesized set functions must be checked for failures when their values are demanded.
The extensions presented in the previous and this section can be combined without problems. Concrete examples for this combination and more examples for the synthesis techniques presented in this paper are available online.^{8}^{8}8https://github.com/finnteegen/synthesizingsetfunctions In particular, there are also examples for the synthesis of higherorder functions, which we omitted in this paper due to the additional complexity of synthesizing the plural functions of higherorder arguments.
8 Related Work
The problems caused by integrating encapsulated search in functional logic programs are discussed in [8] where the concepts of strong and weak encapsulation are distinguished. Weak encapsulation fits better to declarative programming since the results do not depend on the order of evaluation. Set functions [5] makes the boundaries between different sources of nondeterminism clear. The semantical difficulties caused by nesting set functions are discussed in [10] where a denotational semantics for set functions is presented.
The implementation of backtracking and nondeterminism in functional languages has a long tradition [24]. While earlier approaches concentrated on embedding Prologlike constructs in functional languages (e.g., [17, 23]), the implementation of demanddriven nondeterminism, which is the core of contemporary functional logic languages [2], has been less explored. A monadic implementation of the calltime choice is developed in [12] which is the basis to translate a subset of Curry to Haskell [7]. Due to performance problems with this generic approach, KiCS2, another compiler from Curry to Haskell, is proposed in [9]. Currently, KiCS2 is the only system implementing encapsulated search and set functions according to [10], but the detailed implementation is complex and, thus, difficult to maintain. This fact partially motivated the development of the approach described in this paper.
9 Conclusions
We have presented a technique to synthesize the definition of a set function of any function defined in a Curry program. This is useful to add set functions and encapsulated search to any Curry system so that an explicit handling of set functions in the runtime system is not necessary. Thanks to our method, one can add a better (i.e., less strict) implementation of set functions to the Prologbased Curry implementation PAKCS or simplify the runtime system of the Haskellbased Curry implementation KiCS2.
A disadvantage of our approach is that it increases the size of the transformed program due to the addition of the synthesized code. Considering the fact that the majority of application code is deterministic and not involved in set functions, the increased code size is acceptable. Nevertheless, it is an interesting topic for future work to evaluate this for application programs and try to find better synthesis principles (e.g., for specific classes of operations) which produces less additional code.
Our work has the potential of both immediate and far reaching paybacks. We offer a setbased definition of set functions simpler and more immediate than previous ones. We offer a notion of plural function that is original and natural. We show interesting relationships between the two that allow us to better understand, reason about and compute with these concepts. The immediate consequence is an implementation of set functions competitive with previous proposals.
A more intriguing aspect of our work is the possibility of replacing any nondeterministic function, , in a program with its set function, which is deterministic, by enumerating all the results of . Thus, a (often nondeterministic) functional logic program, would become a deterministic program. A consequence of this change is that the techniques for the implementation of nondeterminism, such as backtracking, bubbling and pulltabbing, which are the output of a tremendous intellectual effort of the last few decades, would become unnecessary.
References
 [1] A. Alqaddoumi, S. Antoy, S. Fischer, and F. Reck. The pulltab transformation. In Proc. of the Third International Workshop on Graph Computation Models, pages 127–132. Enschede, The Netherlands, 2010. Available at http://gcmevents.org/gcm2010/pages/gcm2010preproceedings.pdf.
 [2] S. Antoy, R. Echahed, and M. Hanus. A needed narrowing strategy. Journal of the ACM, 47(4):776–822, 2000.
 [3] S. Antoy and M. Hanus. Functional logic design patterns. In Proc. of the 6th International Symposium on Functional and Logic Programming (FLOPS 2002), pages 67–87. Springer LNCS 2441, 2002.
 [4] S. Antoy and M. Hanus. Overlapping rules and logic variables in functional logic programs. In Proceedings of the 22nd International Conference on Logic Programming (ICLP 2006), pages 87–101. Springer LNCS 4079, 2006.
 [5] S. Antoy and M. Hanus. Set functions for functional logic programming. In Proceedings of the 11th ACM SIGPLAN International Conference on Principles and Practice of Declarative Programming (PPDP’09), pages 73–82. ACM Press, 2009.
 [6] S. Antoy and M. Hanus. Functional logic programming. Communications of the ACM, 53(4):74–85, 2010.
 [7] B. Braßel, S. Fischer, M. Hanus, and F. Reck. Transforming functional logic programs into monadic functional programs. In Proc. of the 19th International Workshop on Functional and (Constraint) Logic Programming (WFLP 2010), pages 30–47. Springer LNCS 6559, 2011.
 [8] B. Braßel, M. Hanus, and F. Huch. Encapsulating nondeterminism in functional logic computations. Journal of Functional and Logic Programming, 2004(6), 2004.
 [9] 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.
 [10] J. Christiansen, M. Hanus, F. Reck, and D. Seidel. A semantics for weakly encapsulated search in functional logic programs. In Proc. of the 15th International Symposium on Principle and Practice of Declarative Programming (PPDP’13), pages 49–60. ACM Press, 2013.
 [11] J. de Dios Castro and F.J. LópezFraguas. Extra variables can be eliminated from functional logic programs. Electronic Notes in Theoretical Computer Science, 188:3–19, 2007.
 [12] S. Fischer, O. Kiselyov, and C. Shan. Purely functional lazy nondeterministic programming. Journal of Functional programming, 21(4&5):413–465, 2011.
 [13] J.C. GonzálezMoreno, M.T. HortaláGonzález, F.J. LópezFraguas, and M. RodríguezArtalejo. An approach to declarative programming based on a rewriting logic. Journal of Logic Programming, 40:47–87, 1999.
 [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, S. Antoy, B. Braßel, M. Engelke, K. Höppner, J. Koj, P. Niederau, R. Sadre, and F. Steiner. PAKCS: The Portland Aachen Kiel Curry System. Available at http://www.informatik.unikiel.de/~pakcs/, 2017.
 [16] M. Hanus (ed.). Curry: An integrated functional logic language (vers. 0.9.0). Available at http://www.currylanguage.org, 2016.
 [17] R. Hinze. Prolog’s control constructs in a functional setting  axioms and implementation. International Journal of Foundations of Computer Science, 12(2):125–170, 2001.
 [18] H. Hussmann. Nondeterministic algebraic specifications and nonconfluent term rewriting. Journal of Logic Programming, 12:237–255, 1992.
 [19] F.J. LópezFraguas, J. RodríguezHortalá, and J. SánchezHernández. A simple rewrite notion for calltime choice semantics. In Proceedings of the 9th ACM SIGPLAN International Conference on Principles and Practice of Declarative Programming (PPDP’07), pages 197–208. ACM Press, 2007.
 [20] J.J. MorenoNavarro, H. Kuchen, R. Loogen, and M. RodríguezArtalejo. Lazy narrowing in a graph machine. In Proc. Second International Conference on Algebraic and Logic Programming, pages 298–317. Springer LNCS 463, 1990.
 [21] S. Peyton Jones, editor. Haskell 98 Language and Libraries—The Revised Report. Cambridge University Press, 2003.
 [22] A. Riesco and J. RodríguezHortalá. Singular and plural functions for functional logic programming. Theory and Practice of Logic Programming, 14(1):65–116, 2014.
 [23] S. Seres, M. Spivey, and T. Hoare. Algebra of logic programming. In Proc. ICLP’99, pages 184–199. MIT Press, 1999.
 [24] P. Wadler. How to replace failure by a list of successes. In Functional Programming and Computer Architecture, pages 113–128. Springer LNCS 201, 1985.
Appendix 0.A Properties of Plural Functions
This section contains some interesting properties of plural functions.
Lemma 1
The plural function of a function is unique.
Proof
Lemma 2
If and are composable functions, then .
Proof
First, we prove that for any , . Suppose for some sets and . There exists a set such that and . If is some element of , then there exists some in such that is a value of , and there exists some in such that is a value . Consequently is a value of and is an element of . The proof that for any , is similar.
The following claim establishes key relationships between the set and the plural functions of a function.
Theorem 0.A.1
For any function , argument of , and argument of :

and

.
Proof
We prove that .
If , then, by Def. 1,
is a value of , then, by Def. 2,
is an element of .
The proof that is
similar. Hence condition (1) holds.
We now prove that .
For any , if , by Def. 2,
there exists some such that is a value of .
By Def. 1, .
The proof that is similar. Hence condition (2) holds.