% Testing Higher-Order Functional-Logic Operations % Sebastian Fischer (sebf@informatik.uni-kiel.de) This module defines tests that show how to define higher-order functional-logic programs. > module CFLP.Tests.HigherOrder where > > import CFLP > import CFLP.Tests > > import Test.HUnit > > import Prelude hiding ( not, null, head, map, foldr, flip, id ) > import qualified Prelude as P > import CFLP.Types.Bool > import CFLP.Types.List > > tests :: Test > tests = "higher order" ~: test > [ "apply not function" ~: applyNotFunction > , "apply binary constructor" ~: applyBinCons > , "apply non-deterministic choice" ~: applyChoice > , "call-time choice" ~: callTimeChoice > , "map shared unknowns" ~: mapSharedUnknowns > , "member with fold" ~: memberWithFold > , "overApplication" ~: overApplication > , "reverse with foldr" ~: reverseWithFoldr > , "pointfree reverse" ~: pointfreeReverse > , "function conversion" ~: functionConversion > ] The following test simply applies the not function. > applyNotFunction :: Assertion > applyNotFunction = assertResults comp [True] > where > comp :: Computation Bool > comp = apply (fun not) false The following test applies the binary list constructor. > applyBinCons :: Assertion > applyBinCons = assertResults comp [[True]] > where > comp :: Computation [Bool] > comp cs = withUnique $ \u1 u2 -> > apply (apply (fun (^:)) true cs u1) nil cs u2 The following tests applies the binary operator for non-deterministic choice. > applyChoice :: Assertion > applyChoice = assertResults comp [False,True] > where > comp :: Computation Bool > comp cs = withUnique $ \u1 u2 -> > apply (apply (fun (?)) false cs u1) true cs u2 The following test checks whether call-time choice is still obtained when applying the choice combinator using higher-order features. > callTimeChoice :: Assertion > callTimeChoice = assertResults comp [[False,False],[True,True]] > where > comp :: Computation [Bool] > comp cs = withUnique $ \u1 u2 u3 -> > apply (fun two) > (apply (apply (fun (?)) false cs u1) true cs u2) cs u3 > > two :: (Monad m, Generic a) => Nondet cs m a -> Nondet cs m [a] > two x = x ^: x ^: nil The following test maps the function `not` over a list with a duplicated free variable. > mapSharedUnknowns :: Assertion > mapSharedUnknowns = assertResults comp [[True,True],[False,False]] > where > comp :: Computation [Bool] > comp cs = withUnique $ \u -> map (fun not) (two (unknown u)) cs The following test checks the member operation defined using `foldr`. > memberWithFold :: Assertion > memberWithFold = assertResults comp [True,False] > where > comp :: Computation Bool > comp = foldr (fun (?)) failure (true ^: false ^: nil) The following test applies the composition function which is has a function on its right-hand side: > after :: CFLP s => Data s (b -> c) -> Data s (a -> b) -> Data s (a -> c) > after f g = fun (\x cs -> withUnique $ \u -> apply f (apply g x cs u) cs) > > overApplication :: Assertion > overApplication = assertResults comp [True] > where > comp :: Computation Bool > comp = apply (after (fun not) (fun not)) true The following test makes extensive use of higher-order features by implementing the reverse function using `foldr`. ~~~ rev = flip (foldr (\x f l -> f (x:l)) id) [] ~~~ > reverseWithFoldr :: Assertion > reverseWithFoldr = assertResults comp [[True,False,False]] > where > comp :: Computation [Bool] > comp = rev (false ^: false ^: true ^: nil) > > rev :: CFLP s => Data s [Bool] -> Context (Ctx s) -> ID -> Data s [Bool] > rev = flip (fun (foldr (fun (\x f l -> apply f (x ^: l))) (fun id))) nil > > flip :: CFLP s > => Data s (a -> b -> c) -> Data s b -> Data s a > -> Context (Ctx s) -> ID -> Data s c > flip f x y cs = withUnique $ \u -> apply (apply f y cs u) x cs > > id :: Data s a -> Data s a > id x = x The following uses even more higher-order functions by implementing a pointfree version of the above reverse function. ~~~ rev = flip (foldr (flip (flip ((.).(.)) (:))) id) [] ~~~ > pointfreeReverse :: Assertion > pointfreeReverse = assertResults comp [[True,False,False]] > where > comp :: Computation [Bool] > comp = apply rev (false ^: false ^: true ^: nil) > > rev :: CFLP s => Data s ([Bool] -> [Bool]) > rev = fun (flip (fun (foldr (fun (flip (fun (flip > (fun after `after` fun after) (fun (^:)))))) (fun id))) nil) The following test converts primitive Haskell functions to non-deterministic ones and applies them to non-deterministic values. > functionConversion :: Assertion > functionConversion = assertResults comp [False] > where > comp :: Computation Bool > comp cs = withUnique $ \u -> > apply (foldr (fun after) (fun id) > (nondet [P.not,P.not,P.not]) cs u) > true cs