-- |
-- Module : Conjure
-- Copyright : (c) 2021-2025 Rudy Matela
-- License : 3-Clause BSD (see the file LICENSE)
-- Maintainer : Rudy Matela <rudy@matela.com.br>
--
-- A library for Conjuring function implementations
-- from tests or partial definitions.
-- (a.k.a.: functional inductive programming)
--
-- Step 1: declare your partial function
--
-- > factorial :: Int -> Int
-- > factorial 2 = 2
-- > factorial 3 = 6
-- > factorial 4 = 24
--
-- Step 2: declare a list with the potential building blocks:
--
-- > primitives :: [Prim]
-- > primitives =
-- > [ pr (0::Int)
-- > , pr (1::Int)
-- > , prim "+" ((+) :: Int -> Int -> Int)
-- > , prim "*" ((*) :: Int -> Int -> Int)
-- > , prim "-" ((-) :: Int -> Int -> Int)
-- > ]
--
-- Step 3: call 'conjure' and see your generated function:
--
-- > > conjure "factorial" factorial primitives
-- > factorial :: Int -> Int
-- > -- 0.1s, testing 4 combinations of argument values
-- > -- 0.8s, pruning with 27/65 rules
-- > -- 0.8s, 3 candidates of size 1
-- > -- 0.9s, 3 candidates of size 2
-- > -- 0.9s, 7 candidates of size 3
-- > -- 0.9s, 8 candidates of size 4
-- > -- 0.9s, 28 candidates of size 5
-- > -- 0.9s, 35 candidates of size 6
-- > -- 0.9s, 167 candidates of size 7
-- > -- 0.9s, tested 95 candidates
-- > factorial 0 = 1
-- > factorial x = x * factorial (x - 1)
--
-- The above example takes less than a second to run in a modern laptop.
--
-- Factorial is discovered from scratch through a search.
-- We prune the search space using properties discovered
-- from the results of testing.
--
-- Conjure is not limited to integers,
-- it works for functions over algebraic data types too.
-- See:
--
-- > take' :: Int -> [a] -> [a]
-- > take' 0 [x] = []
-- > take' 1 [x] = [x]
-- > take' 0 [x,y] = []
-- > take' 1 [x,y] = [x]
-- > take' 2 [x,y] = [x,y]
-- > take' 3 [x,y] = [x,y]
--
-- > > conjure "take" (take' :: Int -> [A] -> [A])
-- > > [ pr (0 :: Int)
-- > > , pr (1 :: Int)
-- > > , pr ([] :: [A])
-- > > , prim ":" ((:) :: A -> [A] -> [A])
-- > > , prim "-" ((-) :: Int -> Int -> Int)
-- > > ]
-- > take :: Int -> [A] -> [A]
-- > -- testing 153 combinations of argument values
-- > -- pruning with 4/7 rules
-- > -- ... ... ... ... ... ...
-- > -- 0.4s, 6 candidates of size 8
-- > -- 0.4s, 5 candidates of size 9
-- > -- 0.4s, tested 15 candidates
-- > take 0 xs = []
-- > take x [] = []
-- > take x (y:xs) = y:take (x - 1) xs
--
-- The above example also takes less than a second to run in a modern laptop.
-- The selection of functions in the list of primitives was minimized
-- to what was absolutely needed here.
-- With a larger collection as primitives YMMV.
--
-- Conjure works for user-defined algebraic data types too,
-- given that they are made instances of the 'Conjurable' typeclass.
-- For types without data invariants,
-- it should be enough to call 'deriveConjurable'
-- to create an instance using TH.
{-# LANGUAGE CPP #-}
module Conjure
(
-- * Basic use
conjure
, Prim
, pr
, prim
, guard
, prif
, primOrdCaseFor
-- * Advanced use
, conjureWithMaxSize
, conjureWith
, Args(..)
, args
-- * Conjuring from a specification
, conjureFromSpec
, conjureFromSpecWith
-- * When using custom types
, Conjurable (conjureExpress, conjureEquality, conjureTiers, conjureCases, conjureSubTypes, conjureSize)
, Expr
, val
, value
, reifyExpress
, reifyEquality
, reifyTiers
, conjureType
, Name (..)
, Express (..)
, deriveConjurable
, deriveConjurableIfNeeded
, deriveConjurableCascading
-- * Pure interfaces
, Results (..)
, conjpure
, conjpureWith
-- * Helper test types
, A, B, C, D, E, F
)
where
import Conjure.Engine
import Conjure.Conjurable
import Conjure.Prim
import Conjure.Conjurable.Derive