code-conjure-0.2.6: src/Conjure/Spec.hs
-- |
-- Module : Conjure.Spec
-- Copyright : (c) 2021 Rudy Matela
-- License : 3-Clause BSD (see the file LICENSE)
-- Maintainer : Rudy Matela <rudy@matela.com.br>
--
-- 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
-- | 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 :: [Expr]
-- > sumPrimitives =
-- > [ value "null" (null :: [Int] -> Bool)
-- > , val (0::Int)
-- > , value "+" ((+) :: Int -> Int -> Int)
-- > , value "head" (head :: [Int] -> Int)
-- > , value "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, Conjurable a, Conjurable b)
=> String -> Spec1 a b -> [Expr] -> 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 =
-- > [ value "null" (null :: [Int] -> Bool)
-- > , value ":" ((:) :: Int -> [Int] -> [Int])
-- > , value "head" (head :: [Int] -> Int)
-- > , value "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
, Conjurable b, Eq b, Show b
, Conjurable c, Eq c
) => String -> Spec2 a b c -> [Expr] -> IO ()
conjure2 = conjure2With args
-- | Conjures a three argument function from a specification.
--
-- (cf. 'Spec3', 'conjure3With')
conjure3 :: ( Conjurable a, Eq a, Show a
, Conjurable b, Eq b, Show b
, Conjurable c, Eq c, Show c
, Conjurable d, Eq d
) => String -> Spec3 a b c d -> [Expr] -> IO ()
conjure3 = conjure3With args
-- | Like 'conjure1' but allows setting options through 'Args'/'args'.
conjure1With :: (Eq a, Eq b, Show a, Conjurable a, Conjurable b)
=> Args -> String -> Spec1 a b -> [Expr] -> 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
, Conjurable b, Eq b, Show b
, Conjurable c, Eq c
) => Args -> String -> Spec2 a b c -> [Expr] -> 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
, Conjurable b, Eq b, Show b
, Conjurable c, Eq c, Show c
, Conjurable d, Eq d
) => Args -> String -> Spec3 a b c d -> [Expr] -> 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