-- | -- Module : Conjure.Spec -- Copyright : (c) 2021 Rudy Matela -- License : 3-Clause BSD (see the file LICENSE) -- Maintainer : Rudy Matela -- -- An internal module of 'Conjure', -- a library for Conjuring function implementations -- from tests or partial definitions. -- (a.k.a.: functional inductive programming) module Conjure.Spec ( Spec1 , Spec2 , Spec3 , (-=) , conjure1 , conjure2 , conjure3 , conjure1With , conjure2With , conjure3With ) where import Conjure.Engine import Conjure.Conjurable import Conjure.Utils import Conjure.Prim -- | A partial specification of a function with one argument: -- -- > sumSpec :: Spec1 [Int] Int -- > sumSpec = -- > [ [] -= 0 -- > , [1,2] -= 3 -- > , [3,4,5] -= 12 -- > ] -- -- To be passed as one of the arguments to 'conjure1'. type Spec1 a b = [(a,b)] -- | A partial specification of a function with two arguments: -- -- > appSpec :: Spec2 [Int] [Int] [Int] -- > appSpec = -- > [ (,) [] [0,1] -= [0,1] -- > , (,) [2,3] [] -= [2,3] -- > , (,) [4,5,6] [7,8,9] -= [4,5,6,7,8,9] -- > ] -- -- To be passed as one of the arguments to 'conjure2'. type Spec2 a b c = [((a,b),c)] -- | A partial specification of a function with three arguments. -- -- To be passed as one of the arguments to 'conjure3' type Spec3 a b c d = [((a,b,c),d)] -- | To be used when constructing specifications: -- 'Spec1', -- 'Spec2' and -- 'Spec3'. (-=) :: a -> b -> (a,b) (-=) = (,) -- | Conjures a one argument function from a specification. -- -- Given: -- -- > sumSpec :: Spec1 [Int] Int -- > sumSpec = -- > [ [] -= 0 -- > , [1,2] -= 3 -- > , [3,4,5] -= 12 -- > ] -- -- > sumPrimitives :: [Prim] -- > sumPrimitives = -- > [ prim "null" (null :: [Int] -> Bool) -- > , pr (0::Int) -- > , prim "+" ((+) :: Int -> Int -> Int) -- > , prim "head" (head :: [Int] -> Int) -- > , prim "tail" (tail :: [Int] -> [Int]) -- > ] -- -- Then: -- -- > > conjure1 "sum" sumSpec sumPrimitives -- > sum :: [Int] -> Int -- > -- testing 3 combinations of argument values -- > -- ... -- > -- looking through 189/465 candidates of size 10 -- > xs ++ ys = if null xs then ys else head xs:(tail xs ++ ys) -- -- (cf. 'Spec1', 'conjure1With') conjure1 :: (Eq a, Eq b, Show a, Express a, Conjurable a, Conjurable b) => String -> Spec1 a b -> [Prim] -> IO () conjure1 = conjure1With args -- | Conjures a two argument function from a specification. -- -- Given: -- -- > appSpec :: Spec2 [Int] [Int] [Int] -- > appSpec = -- > [ (,) [] [0,1] -= [0,1] -- > , (,) [2,3] [] -= [2,3] -- > , (,) [4,5,6] [7,8,9] -= [4,5,6,7,8,9] -- > ] -- -- > appPrimitives :: [Expr] -- > appPrimitives = -- > [ prim "null" (null :: [Int] -> Bool) -- > , prim ":" ((:) :: Int -> [Int] -> [Int]) -- > , prim "head" (head :: [Int] -> Int) -- > , prim "tail" (tail :: [Int] -> [Int]) -- > ] -- -- Then: -- -- > > conjure2 "++" appSpec appPrimitives -- > (++) :: [Int] -> [Int] -> [Int] -- > -- testing 3 combinations of argument values -- > -- ... -- > -- looking through 26166/57090 candidates of size 11 -- > xs ++ ys = if null xs then ys else head xs:(tail xs ++ ys) -- -- (cf. 'Spec2', 'conjure2With') conjure2 :: ( Conjurable a, Eq a, Show a, Express a , Conjurable b, Eq b, Show b, Express b , Conjurable c, Eq c ) => String -> Spec2 a b c -> [Prim] -> IO () conjure2 = conjure2With args -- | Conjures a three argument function from a specification. -- -- (cf. 'Spec3', 'conjure3With') conjure3 :: ( Conjurable a, Eq a, Show a, Express a , Conjurable b, Eq b, Show b, Express b , Conjurable c, Eq c, Show c, Express c , Conjurable d, Eq d ) => String -> Spec3 a b c d -> [Prim] -> IO () conjure3 = conjure3With args -- | Like 'conjure1' but allows setting options through 'Args'/'args'. conjure1With :: (Eq a, Eq b, Show a, Express a, Conjurable a, Conjurable b) => Args -> String -> Spec1 a b -> [Prim] -> IO () conjure1With args nm bs = conjureWith args{forceTests=ts} nm (mkFun1 bs) where ts = [[val x] | (x,_) <- bs] -- | Like 'conjure2' but allows setting options through 'Args'/'args'. conjure2With :: ( Conjurable a, Eq a, Show a, Express a , Conjurable b, Eq b, Show b, Express b , Conjurable c, Eq c ) => Args -> String -> Spec2 a b c -> [Prim] -> IO () conjure2With args nm bs = conjureWith args{forceTests=ts} nm (mkFun2 bs) where ts = [[val x, val y] | ((x,y),_) <- bs] -- | Like 'conjure3' but allows setting options through 'Args'/'args'. conjure3With :: ( Conjurable a, Eq a, Show a, Express a , Conjurable b, Eq b, Show b, Express b , Conjurable c, Eq c, Show c, Express c , Conjurable d, Eq d ) => Args -> String -> Spec3 a b c d -> [Prim] -> IO () conjure3With args nm bs = conjureWith args{forceTests=ts} nm (mkFun3 bs) where ts = [[val x, val y, val z] | ((x,y,z),_) <- bs] mkFun1 :: Eq a => [(a,b)] -> a -> b mkFun1 bs = \x -> fromMaybe undefined $ lookup x bs mkFun2 :: (Eq a, Eq b) => [((a,b),c)] -> a -> b -> c mkFun2 bs = \x y -> fromMaybe undefined $ lookup (x,y) bs mkFun3 :: (Eq a, Eq b, Eq c) => [((a,b,c),d)] -> a -> b -> c -> d mkFun3 bs = \x y z -> fromMaybe undefined $ lookup (x,y,z) bs