code-conjure-0.7.6: src/Conjure/Settings.hs
-- |
-- Module : Conjure.Settings
-- Copyright : (c) 2021-2025 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)
--
-- This contains the settings for functions in "Conjure.Engine".
{-# LANGUAGE CPP, RecordWildCards, TupleSections #-}
module Conjure.Settings
(
-- * Basic settings
maxTests
, maxSize
, target
-- * Advanced settings
, maxRecursions
, maxEquationSize
, maxSearchTests
, maxDeconstructionSize
, maxConstantSize
, maxPatternSize
, maxPatternDepth
-- * Debug options
, showCandidates
, showTheory
, singlePattern
, showTests
, showPatterns
, showDeconstructions
, carryOn
-- * Pruning options
, dontRewrite
, dontRequireDescent
, omitAssortedPruning
, maxEarlyTests
, dontCopyBindings
, nonAtomicNumbers
, uniqueCandidates
-- * Filtering settings
, actual
-- * Read basic settings
, maxTestsI
, targetAndMaxSizeI
-- * Read advanced settings
, maxRecursionsI
, maxEquationSizeI
, maxSearchTestsI
, maxDeconstructionSizeI
, maxConstantSizeI
, maxPatternSizeI
, maxPatternDepthI
-- * Read debug options
, showCandidatesI
, showTheoryI
, singlePatternI
, showTestsI
, showPatternsI
, showDeconstructionsI
, carryOnI
-- * Read pruning options
, rewriteI
, requireDescentI
, assortedPruningI
, maxEarlyTestsI
, copyBindingsI
, atomicNumbersI
, uniqueCandidatesI
)
where
import Conjure.Utils
import Data.Express (val, eval, typ)
import Conjure.Ingredient (Ingredient)
-- | Arguments to be passed to
-- 'Conjure.conjureWith' or 'Conjure.conjpureWith'.
-- You should use smart constructors instead.
data Setting
= Noop -- ^ internal use: no-op setting
| MaxTests Int -- ^ maximum number of tests to each candidate
| MaxSize Int -- ^ maximum size of candidate bodies
| Target Int -- ^ enumerate further sizes of candidates until this target
| MaxRecursions Int -- ^ maximum number of recursive evaluations when testing candidates
| MaxEquationSize Int -- ^ maximum size of equation operands
| MaxSearchTests Int -- ^ maximum number of tests to search for defined values
| MaxDeconstructionSize Int -- ^ maximum size of deconstructions (e.g.: @- 1@)
| MaxConstantSize Int -- ^ maximum size of constants (0 for no limit)
| MaxPatternSize Int -- ^ maximum size of patterns (0 for no limit)
| MaxPatternDepth Int -- ^ maximum depth of patterns
-- advanced & debug options --
| CarryOn -- ^ carry on after finding a suitable candidate
| ShowTheory -- ^ show theory discovered by Speculate used in pruning
| SinglePattern -- ^ restrict candidates to a single pattern
| ShowCandidates -- ^ (debug) show candidates -- warning: wall of text
| ShowTests -- ^ (debug) show tests
| ShowPatterns -- ^ (debug) show possible LHS patterns
| ShowDeconstructions -- ^ (debug) show conjectured-and-allowed deconstructions
-- pruning options --
| DontRewrite -- ^ turns off unique-modulo-rewriting candidates
| DontRequireDescent -- ^ require recursive calls to deconstruct arguments
| OmitAssortedPruning -- ^ omit other assorted pruning rules
| MaxEarlyTests Int -- ^ don't perform tests early-and-independently on each binding
| DontCopyBindings -- ^ don't copy partial definition bindings in candidates
| AtomicNumbers -- ^ restrict constant/ground numeric expressions to atoms
| NonAtomicNumbers -- ^ lift constant/ground numetic expression restrictions
| UniqueCandidates -- ^ unique-modulo-testing candidates
deriving (Eq, Ord, Show, Read)
-- | Constructs an ingredient from a setting
setting :: Setting -> Ingredient
setting x = (val x, error "Conjure.Settings: evaluating reification, this is a bug")
-- using 'id' instead of 'error' above would work,
-- but we want to be warned in case we accidentally evaluate the reification
extract :: Ingredient -> Setting
extract = eval Noop . fst
-- | Lists actual incredients in the list
actual :: [Ingredient] -> [Ingredient]
actual is = [i | i <- is, typ (fst i) /= typeOf Noop]
-- | By default,
-- 'Conjure.conjure' tests candidates up to a maximum of 360 tests.
-- This configures the maximum number of tests to each candidate,
-- when provided in the list of ingredients:
--
-- > conjure "..." ... [ ...
-- > , maxTests 1080
-- > , ... ]
maxTests :: Int -> Ingredient
maxTests = setting . MaxTests
-- | Finds the set maximum number of tests or set the default of 360
maxTestsI :: [Ingredient] -> Int
maxTestsI is = headOr 360 [m | MaxTests m <- map extract is]
-- the use of magic numbers goes well with the theme of Conjure.
-- | By default,
-- 'Conjure.conjure' imposes no limit on the size of candidates.
--
-- This configures a different maximum
-- when provided in the list of ingredients.
--
-- If only one of 'maxSize' and 'target' is defined,
-- it is used. If none, target is used.
maxSize :: Int -> Ingredient
maxSize = setting . MaxSize
-- | By default, 'Conjure.conjure' targets testing 10080 candidates.
-- This configures a different target when
-- provided in the list of ingredients:
--
-- > conjure "..." ... [ ...
-- > , target 5040
-- > , ... ]
target :: Int -> Ingredient
target = setting . Target
-- | Computes the target and maxSize.
--
-- When none is provided, we default to a target of 10080.
targetAndMaxSizeI :: [Ingredient] -> (Int, Int)
targetAndMaxSizeI is =
case (t, m) of
(0, 0) -> (10080, 0)
(t, m) -> (t, m)
where
t = headOr 0 [m | Target m <- map extract is]
m = headOr 0 [m | MaxSize m <- map extract is]
-- above is a perfect use for the These datatype,
-- one of my favourite non-standard,
-- but I don't want to impose a dependency on my users...
-- | By default,
-- 'Conjure.conjure' evaluates candidates for up to 60 recursive calls.
--
-- This allows overriding the default
-- when provided in the ingredient list.
maxRecursions :: Int -> Ingredient
maxRecursions = setting . MaxRecursions
maxRecursionsI :: [Ingredient] -> Int
maxRecursionsI is = headOr 60 [m | MaxRecursions m <- map extract is]
-- | By default,
-- 'Conjure.conjure' considers equations of up to 5 symbols
-- for pruning-through-rewriting.
--
-- This allows overriding the default:
-- 6 or 7 are also good values for this depending on the number of ingredients.
--
-- > conjure ... ... [ ...
-- > , maxEquationSize 6
-- > , ... ]
--
-- Internally, this is the maximum size passed to the Speculate tool.
maxEquationSize :: Int -> Ingredient
maxEquationSize = setting . MaxEquationSize
maxEquationSizeI :: [Ingredient] -> Int
maxEquationSizeI is = headOr 5 [m | MaxEquationSize m <- map extract is]
-- | By default,
-- 'Conjure.conjure' enumerates up to 110880 argument combinations
-- while reifying the partial definition passed by the user.
--
-- This allows configuring a higher default
-- when provided in the ingredient list.
--
-- Increasing this setting is useful
-- when the partial definition is not exercised enough.
maxSearchTests :: Int -> Ingredient
maxSearchTests = setting . MaxSearchTests
maxSearchTestsI :: [Ingredient] -> Int
maxSearchTestsI is = headOr 110880 [m | MaxSearchTests m <- map extract is]
-- | By default
-- 'Conjure.conjure' allows deconstruction expressions
-- of up to 4 symbols.
--
-- This allows overriding the default
-- when provided in the ingredient list.
maxDeconstructionSize :: Int -> Ingredient
maxDeconstructionSize = setting . MaxDeconstructionSize
maxDeconstructionSizeI :: [Ingredient] -> Int
maxDeconstructionSizeI is = headOr 4 [m | MaxDeconstructionSize m <- map extract is]
-- | Configures a maximum size of constant sub-expressions
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
maxConstantSize :: Int -> Ingredient
maxConstantSize = setting . MaxConstantSize
maxConstantSizeI :: [Ingredient] -> Int
maxConstantSizeI is = headOr 0 [m | MaxConstantSize m <- map extract is]
-- | By default,
-- 'Conjure.conjure' places no limit in the LHS pattern sizes.
--
-- This allows configuring a limit when provided in the ingredient list
maxPatternSize :: Int -> Ingredient
maxPatternSize = setting . MaxPatternSize
maxPatternSizeI :: [Ingredient] -> Int
maxPatternSizeI is = headOr 0 [m | MaxPatternSize m <- map extract is]
-- | By default,
-- 'Conjure.conjure' enumerates pattern breakdowns of the outernmost constructor
-- of depth 1.
--
-- This allows overriding the default when provided in the ingredient list:
-- a depth of 2 allows breakdowns of the two outernmost constructors;
-- a depth of 3, three outernmost constructors;
-- etc.
maxPatternDepth :: Int -> Ingredient
maxPatternDepth = setting . MaxPatternDepth
maxPatternDepthI :: [Ingredient] -> Int
maxPatternDepthI is = headOr 1 [m | MaxPatternDepth m <- map extract is]
-- | Carry on after finding a suitable candidate.
-- To be provided as a setting in the list of ingredients.
carryOn :: Ingredient
carryOn = setting CarryOn
carryOnI :: [Ingredient] -> Bool
carryOnI is = notNull [True | CarryOn <- map extract is]
-- | (Debug option).
-- Shows the underlying theory used in pruning
-- when this is provided in the ingredient list.
showTheory :: Ingredient
showTheory = setting ShowTheory
showTheoryI :: [Ingredient] -> Bool
showTheoryI is = notNull [True | ShowTheory <- map extract is]
-- | (Debug option)
-- When provided in the ingredient list,
-- this reverts to a legacy enumeration that
-- contains candidates with a single LHS matching everything.
singlePattern :: Ingredient
singlePattern = setting SinglePattern
singlePatternI :: [Ingredient] -> Bool
singlePatternI is = notNull [True | SinglePattern <- map extract is]
-- | (Debug option)
-- When provided in the ingredients list,
-- this enables showing enumerated candidates.
--
-- > conjure ... ... [ ...
-- > , showCandidates
-- > , ... ]
--
-- Warning: activating this will likely produce a humongous wall-of-text.
showCandidates :: Ingredient
showCandidates = setting ShowCandidates
showCandidatesI :: [Ingredient] -> Bool
showCandidatesI is = notNull [True | ShowCandidates <- map extract is]
-- | (Debug option)
-- When provided in the ingredients list,
-- 'Conjure.conjure' will print the tests reified from the partial definition.
-- (cf. 'maxTests', 'maxSearchTests')
showTests :: Ingredient
showTests = setting ShowTests
showTestsI :: [Ingredient] -> Bool
showTestsI is = notNull [True | ShowTests <- map extract is]
-- | (Debug option)
-- When this option is provided in the ingredients list,
-- 'Conjure.conjure' will print the enumrated LHS patterns.
-- (cf. 'maxPatternSize', 'maxPatternDepth')
showPatterns :: Ingredient
showPatterns = setting ShowPatterns
showPatternsI :: [Ingredient] -> Bool
showPatternsI is = notNull [True | ShowPatterns <- map extract is]
-- | (Debug option)
-- Makes 'Conjure.conjure' print enumerated deconstructions
-- when provided in its ingredient list.
showDeconstructions :: Ingredient
showDeconstructions = setting ShowDeconstructions
showDeconstructionsI :: [Ingredient] -> Bool
showDeconstructionsI is = notNull [True | ShowDeconstructions <- map extract is]
-- | Disables rewriting-as-pruning
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
dontRewrite :: Ingredient
dontRewrite = setting DontRewrite
rewriteI :: [Ingredient] -> Bool
rewriteI is = null [False | DontRewrite <- map extract is]
-- | Disables the recursive descent requirement
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
dontRequireDescent :: Ingredient
dontRequireDescent = setting DontRequireDescent
requireDescentI :: [Ingredient] -> Bool
requireDescentI is = null [False | DontRequireDescent <- map extract is]
-- | Disables assorted pruning rules
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
omitAssortedPruning :: Ingredient
omitAssortedPruning = setting OmitAssortedPruning
assortedPruningI :: [Ingredient] -> Bool
assortedPruningI is = null [False | OmitAssortedPruning <- map extract is]
-- | Sets the maximum number of early tests
-- performed independently bindings/equations
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
--
-- When not set, this defaults to a modest 12.
maxEarlyTests :: Int -> Ingredient
maxEarlyTests = setting . MaxEarlyTests
maxEarlyTestsI :: [Ingredient] -> Int
maxEarlyTestsI is = headOr 12 [m | MaxEarlyTests m <- map extract is]
-- | Disables the copy-bindings rule
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
dontCopyBindings :: Ingredient
dontCopyBindings = setting DontCopyBindings
copyBindingsI :: [Ingredient] -> Bool
copyBindingsI is = null [False | DontCopyBindings <- map extract is]
atomicNumbers :: Ingredient
atomicNumbers = setting AtomicNumbers
-- | Disables the requirement of atomic numeric expressions
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
-- (cf. 'maxConstantSize')
nonAtomicNumbers :: Ingredient
nonAtomicNumbers = setting NonAtomicNumbers
atomicNumbersI :: [Ingredient] -> Bool
atomicNumbersI is = null [False | NonAtomicNumbers <- map extract is]
-- | Enables expensive unique-modulo-testing candidates
-- when provided in the ingredient list
-- of 'Conjure.conjure' or 'Conjure.conjureFromSpec'.
--
-- Warning: this makes 'Conjure.conjure' very slow,
-- it is only intended for approximating the theoretical
-- limits of pruning in toy examples.
uniqueCandidates :: Ingredient
uniqueCandidates = setting UniqueCandidates
uniqueCandidatesI :: [Ingredient] -> Bool
uniqueCandidatesI is = notNull [True | UniqueCandidates <- map extract is]