------------------------------------------------------------------------ --- This module contains an implementation of set functions. --- The general idea of set functions is described in: --- --- > S. Antoy, M. Hanus: Set Functions for Functional Logic Programming --- > Proc. 11th International Conference on Principles and Practice --- > of Declarative Programming (PPDP'09), pp. 73-82, ACM Press, 2009 --- --- The general concept of set functions is as follows. --- If `f` is an n-ary function, then `(setn f)` is a set-valued --- function that collects all non-determinism caused by f (but not --- the non-determinism caused by evaluating arguments!) in a set. --- Thus, `(setn f a1 ... an)` returns the set of all --- values of `(f b1 ... bn)` where `b1`,...,`bn` are values --- of the arguments `a1`,...,`an` (i.e., the arguments are --- evaluated "outside" this capsule so that the non-determinism --- caused by evaluating these arguments is not captured in this capsule --- but yields several results for `(setn...)`. --- Similarly, logical variables occuring in `a1`,...,`an` are not bound --- inside this capsule (in PAKCS they cause a suspension until --- they are bound). --- --- *Remark:* --- Since there is no special syntax for set functions, --- one has to write `(setn f)` for the set function of the --- _n-ary top-level function_ `f`. --- The correct usage of set functions is currently not checked by --- the compiler, i.e., one can also write unintended uses --- like `set0 ((+1) (1 ? 2))`. --- In order to check the correct use of set functions, --- it is recommended to apply the tool --- [CurryCheck](https://cpm.curry-lang.org/pkgs/currycheck.html) --- on Curry programs which reports illegal uses of set functions --- (among other properties). --- --- The set of values returned by a set function is represented --- by an abstract type 'Values' on which several operations are --- defined in this module. Actually, it is a multiset of values, --- i.e., duplicates are not removed. --- --- The handling of failures and nested occurrences of set functions --- is not specified in the previous paper. Thus, a detailed description --- of the semantics of set functions as implemented in this library --- can be found in the paper --- --- > J. Christiansen, M. Hanus, F. Reck, D. Seidel: --- > A Semantics for Weakly Encapsulated Search in Functional Logic Programs --- > Proc. 15th International Conference on Principles and Practice --- > of Declarative Programming (PPDP'13), pp. 49-60, ACM Press, 2013 --- --- Note that the implementation of this library uses multisets --- instead of sets. Thus, the result of a set function might --- contain multiple values. From a declarative point of view, --- this is not relevant. It has the advantage that equality --- is not required on values, i.e., encapsulated values can also --- be functional. --- --- The PAKCS implementation of set functions has several restrictions, --- in particular: --- --- 1. The multiset of values is completely evaluated when demanded. --- Thus, if it is infinite, its evaluation will not terminate --- even if only some elements (e.g., for a containment test) --- are demanded. However, for the emptiness test, at most one --- value will be computed --- 2. The arguments of a set function are strictly evaluated before --- the set functions itself will be evaluated. --- 3. If the multiset of values contains unbound variables, --- the evaluation suspends. --- --- @author Michael Hanus, Fabian Reck --- @version November 2022 ------------------------------------------------------------------------ {-# LANGUAGE CPP #-} {-# OPTIONS_FRONTEND -Wno-incomplete-patterns #-} module Control.SetFunctions (set0, set1, set2, set3, set4, set5, set6, set7 #ifdef __KICS2__ , set0With, set1With, set2With, set3With, set4With, set5With, set6With , set7With #endif , Values, isEmpty, notEmpty, valueOf , chooseValue, choose, selectValue, select, getSomeValue, getSome , mapValues, foldValues, filterValues , minValue, minValueBy, maxValue, maxValueBy , values2list, printValues, sortValues, sortValuesBy ) where import Data.List ( delete, minimum, minimumBy, maximum, maximumBy, sortBy ) #ifdef __KICS2__ import Control.SearchTree #else import Control.AllValues ( allValues, oneValue ) #endif ------------------------------------------------------------------------ --- Combinator to transform a 0-ary function into a corresponding set function. set0 :: b -> Values b #ifdef __KICS2__ set0 f = set0With dfsStrategy f --- Combinator to transform a 0-ary function into a corresponding set function --- that uses a given strategy to compute its values. set0With :: Strategy b -> b -> Values b set0With s f = Values (vsToList (s (someSearchTree f))) #else set0 f = Values (oneValue f) (allValues f) #endif --- Combinator to transform a unary function into a corresponding set function. set1 :: (a1 -> b) -> a1 -> Values b #ifdef __KICS2__ set1 f x = set1With dfsStrategy f x --- Combinator to transform a unary function into a corresponding set function --- that uses a given strategy to compute its values. set1With :: Strategy b -> (a1 -> b) -> a1 -> Values b set1With s f x = allVs s (\_ -> f x) #else set1 f x | isVal x = Values (oneValue (f x)) (allValues (f x)) #endif --- Combinator to transform a binary function into a corresponding set function. set2 :: (a1 -> a2 -> b) -> a1 -> a2 -> Values b #ifdef __KICS2__ set2 f x1 x2 = set2With dfsStrategy f x1 x2 --- Combinator to transform a binary function into a corresponding set function --- that uses a given strategy to compute its values. set2With :: Strategy b -> (a1 -> a2 -> b) -> a1 -> a2 -> Values b set2With s f x1 x2 = allVs s (\_ -> f x1 x2) #else set2 f x1 x2 | isVal x1 & isVal x2 = Values (oneValue (f x1 x2)) (allValues (f x1 x2)) #endif --- Combinator to transform a function of arity 3 --- into a corresponding set function. set3 :: (a1 -> a2 -> a3 -> b) -> a1 -> a2 -> a3 -> Values b #ifdef __KICS2__ set3 f x1 x2 x3 = set3With dfsStrategy f x1 x2 x3 --- Combinator to transform a function of arity 3 --- into a corresponding set function --- that uses a given strategy to compute its values. set3With :: Strategy b -> (a1 -> a2 -> a3 -> b) -> a1 -> a2 -> a3 -> Values b set3With s f x1 x2 x3 = allVs s (\_ -> f x1 x2 x3) #else set3 f x1 x2 x3 | isVal x1 & isVal x2 & isVal x3 = Values (oneValue (f x1 x2 x3)) (allValues (f x1 x2 x3)) #endif --- Combinator to transform a function of arity 4 --- into a corresponding set function. set4 :: (a1 -> a2 -> a3 -> a4 -> b) -> a1 -> a2 -> a3 -> a4 -> Values b #ifdef __KICS2__ set4 f x1 x2 x3 x4 = set4With dfsStrategy f x1 x2 x3 x4 --- Combinator to transform a function of arity 4 --- into a corresponding set function --- that uses a given strategy to compute its values. set4With :: Strategy b -> (a1 -> a2 -> a3 -> a4 -> b) -> a1 -> a2 -> a3 -> a4 -> Values b set4With s f x1 x2 x3 x4 = allVs s (\_ -> f x1 x2 x3 x4) #else set4 f x1 x2 x3 x4 | isVal x1 & isVal x2 & isVal x3 & isVal x4 = Values (oneValue (f x1 x2 x3 x4)) (allValues (f x1 x2 x3 x4)) #endif --- Combinator to transform a function of arity 5 --- into a corresponding set function. set5 :: (a1 -> a2 -> a3 -> a4 -> a5 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> Values b #ifdef __KICS2__ set5 f x1 x2 x3 x4 x5 = set5With dfsStrategy f x1 x2 x3 x4 x5 --- Combinator to transform a function of arity 5 --- into a corresponding set function --- that uses a given strategy to compute its values. set5With :: Strategy b -> (a1 -> a2 -> a3 -> a4 -> a5 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> Values b set5With s f x1 x2 x3 x4 x5 = allVs s (\_ -> f x1 x2 x3 x4 x5) #else set5 f x1 x2 x3 x4 x5 | isVal x1 & isVal x2 & isVal x3 & isVal x4 & isVal x5 = Values (oneValue (f x1 x2 x3 x4 x5)) (allValues (f x1 x2 x3 x4 x5)) #endif --- Combinator to transform a function of arity 6 --- into a corresponding set function. set6 :: (a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> Values b #ifdef __KICS2__ set6 f x1 x2 x3 x4 x5 x6 = set6With dfsStrategy f x1 x2 x3 x4 x5 x6 --- Combinator to transform a function of arity 6 --- into a corresponding set function --- that uses a given strategy to compute its values. set6With :: Strategy b -> (a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> Values b set6With s f x1 x2 x3 x4 x5 x6 = allVs s (\_ -> f x1 x2 x3 x4 x5 x6) #else set6 f x1 x2 x3 x4 x5 x6 | isVal x1 & isVal x2 & isVal x3 & isVal x4 & isVal x5 & isVal x6 = Values (oneValue (f x1 x2 x3 x4 x5 x6)) (allValues (f x1 x2 x3 x4 x5 x6)) #endif --- Combinator to transform a function of arity 7 --- into a corresponding set function. set7 :: (a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> Values b #ifdef __KICS2__ set7 f x1 x2 x3 x4 x5 x6 x7 = set7With dfsStrategy f x1 x2 x3 x4 x5 x6 x7 --- Combinator to transform a function of arity 7 --- into a corresponding set function --- that uses a given strategy to compute its values. set7With :: Strategy b -> (a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> b) -> a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 -> Values b set7With s f x1 x2 x3 x4 x5 x6 x7 = allVs s (\_ -> f x1 x2 x3 x4 x5 x6 x7) #else set7 f x1 x2 x3 x4 x5 x6 x7 | isVal x1 & isVal x2 & isVal x3 & isVal x4 & isVal x5 & isVal x6 & isVal x7 = Values (oneValue (f x1 x2 x3 x4 x5 x6 x7)) (allValues (f x1 x2 x3 x4 x5 x6 x7)) #endif ------------------------------------------------------------------------ -- Auxiliaries: #ifdef __KICS2__ -- Collect all values of an expression (represented as a constant function) -- in a list: allVs :: Strategy a -> (() -> a) -> Values a allVs s f = Values (vsToList ((incDepth $!! s) ((incDepth $!! someSearchTree) ((incDepth $!! f) ())))) -- Apply a function to an argument where the encapsulation level of the -- argument is incremented. incDepth :: (a -> b) -> a -> b incDepth external #else -- Returns `True` after evaluating the argument to a ground value. isVal :: a -> Bool isVal x = (id $## x) `seq` True #endif ------------------------------------------------------------------------ --- Abstract type representing multisets of values. #ifdef __KICS2__ -- In KiCS2, values are represented as (possibly infinite) lists. data Values a = Values [a] #else -- In PAKCS, values are represented as lists but the first argument -- is used if values are tested for emptiness of a single value -- is selected. This has the advantage that one can deal with infinite -- search spaces as long as one is only interested in an emptiness -- test or a single value. data Values a = Values (Maybe a) [a] #endif --- Internal operation to extract all elements of a multiset of values. valuesOf :: Values a -> [a] #ifdef __KICS2__ valuesOf (Values s) = s #else valuesOf (Values _ s) = s #endif ---------------------------------------------------------------------- --- Is a multiset of values empty? isEmpty :: Values a -> Bool #ifdef __PAKCS__ isEmpty (Values firstval _) = case firstval of Nothing -> True Just _ -> False #else isEmpty s = null (valuesOf s) #endif --- Is a multiset of values not empty? notEmpty :: Values a -> Bool notEmpty vs = not (isEmpty vs) --- Is some value an element of a multiset of values? valueOf :: Eq a => a -> Values a -> Bool valueOf e s = e `elem` valuesOf s --- Chooses (non-deterministically) some value in a multiset of values --- and returns the chosen value. For instance, the expression --- --- chooseValue (set1 anyOf [1,2,3]) --- --- non-deterministically evaluates to the values `1`, `2`, and `3`. --- Thus, `(set1 chooseValue)` is the identity on value sets, i.e., --- `(set1 chooseValue s)` contains the same elements as the --- value set `s`. chooseValue :: Eq a => Values a -> a chooseValue s = fst (choose s) --- Chooses (non-deterministically) some value in a multiset of values --- and returns the chosen value and the remaining multiset of values. --- Thus, if we consider the operation `chooseValue` defined by --- --- chooseValue x = fst (choose x) --- --- then `(set1 chooseValue)` is the identity on value sets, i.e., --- `(set1 chooseValue s)` contains the same elements as the --- value set `s`. choose :: Eq a => Values a -> (a, Values a) #ifdef __KICS2__ choose (Values vs) = (x, Values xs) #else choose (Values _ vs) = (x, Values (if null xs then Nothing else Just (head xs)) xs) #endif where x = foldr1 (?) vs xs = delete x vs --- Selects (indeterministically) some value in a multiset of values --- and returns the selected value. --- Thus, `selectValue` has always at most one value, i.e., it is --- a deterministic operation. --- It fails if the value set is empty. --- --- **NOTE:** --- The usage of this operation is only safe (i.e., does not destroy --- completeness) if all values in the argument set are identical. selectValue :: Values a -> a #ifdef __PAKCS__ selectValue (Values (Just val) _) = val #else selectValue s = fst (select s) #endif --- Selects (indeterministically) some value in a multiset of values --- and returns the selected value and the remaining multiset of values. --- Thus, `select` has always at most one value, i.e., it is --- a deterministic operation. --- It fails if the value set is empty. --- --- **NOTE:** --- The usage of this operation is only safe (i.e., does not destroy --- completeness) if all values in the argument set are identical. select :: Values a -> (a, Values a) #ifdef __KICS2__ select (Values (x:xs)) = (x, Values xs) #else select (Values _ (x:xs)) = (x, Values (if null xs then Nothing else Just (head xs)) xs) #endif --- Returns (indeterministically) some value in a multiset of values. --- If the value set is empty, `Nothing` is returned. getSomeValue :: Values a -> IO (Maybe a) #ifdef __KICS2__ getSomeValue (Values []) = return Nothing getSomeValue (Values (x:_)) = return (Just x) #else getSomeValue (Values mbval _) = return mbval #endif --- Selects (indeterministically) some value in a multiset of values --- and returns the selected value and the remaining multiset of values. --- Thus, `select` has always at most one value. --- If the value set is empty, `Nothing` is returned. getSome :: Values a -> IO (Maybe (a, Values a)) #ifdef __KICS2__ getSome (Values []) = return Nothing getSome (Values (x:xs)) = return (Just (x, Values xs)) #else getSome (Values _ []) = return Nothing getSome (Values _ (x:xs)) = return (Just (x, Values (if null xs then Nothing else Just (head xs)) xs)) #endif --- Maps a function to all elements of a multiset of values. mapValues :: (a -> b) -> Values a -> Values b #ifdef __KICS2__ mapValues f (Values s) = Values (map f s) #else mapValues f (Values mbval s) = Values (maybe Nothing (Just . f) mbval) (map f s) #endif --- Accumulates all elements of a multiset of values by applying a binary --- operation. This is similarly to fold on lists, but the binary operation --- must be **commutative** so that the result is independent of the order --- of applying this operation to all elements in the multiset. foldValues :: (a -> a -> a) -> a -> Values a -> a foldValues f z s = foldr f z (valuesOf s) --- Keeps all elements of a multiset of values that satisfy a predicate. filterValues :: (a -> Bool) -> Values a -> Values a #ifdef __KICS2__ filterValues p (Values s) = Values (filter p s) #else filterValues p (Values _ s) = Values val xs where xs = filter p s val = if null xs then Nothing else Just (head xs) #endif --- Returns the minimum of a non-empty multiset of values --- according to the given comparison function on the elements. minValue :: Ord a => Values a -> a minValue s = minimum (valuesOf s) --- Returns the minimum of a non-empty multiset of values --- according to the given comparison function on the elements. minValueBy :: (a -> a -> Ordering) -> Values a -> a minValueBy cmp s = minimumBy cmp (valuesOf s) --- Returns the maximum of a non-empty multiset of values --- according to the given comparison function on the elements. maxValue :: Ord a => Values a -> a maxValue s = maximum (valuesOf s) --- Returns the maximum of a non-empty multiset of values --- according to the given comparison function on the elements. maxValueBy :: (a -> a -> Ordering) -> Values a -> a maxValueBy cmp s = maximumBy cmp (valuesOf s) --- Puts all elements of a multiset of values in a list. --- Since the order of the elements in the list might depend on --- the time of the computation, this operation is an I/O action. values2list :: Values a -> IO [a] values2list s = return (valuesOf s) --- Prints all elements of a multiset of values. printValues :: Show a => Values a -> IO () printValues s = values2list s >>= mapM_ print --- Transforms a multiset of values into a list sorted by --- the standard term ordering. As a consequence, the multiset of values --- is completely evaluated. sortValues :: Ord a => Values a -> [a] sortValues = sortValuesBy (<=) --- Transforms a multiset of values into a list sorted by a given ordering --- on the values. As a consequence, the multiset of values --- is completely evaluated. --- In order to ensure that the result of this operation is independent of the --- evaluation order, the given ordering must be a total order. sortValuesBy :: (a -> a -> Bool) -> Values a -> [a] sortValuesBy leq s = sortBy leq (valuesOf s) ------------------------------------------------------------------------