packages feed

typecheck-plugin-nat-simple-0.1.0.0: src/Data/Derivation/CanDerive.hs

{-# LANGUAGE BlockArguments, OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables, TypeApplications #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleContexts, FlexibleInstances #-}
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

module Data.Derivation.CanDerive (
	-- * CAN DERIVE
	canDerive,
	-- * GIVENS
	Givens, givens,
	-- * WANTED
	Wanted, wanted ) where

import Prelude hiding (unwords, log)

import Control.Arrow (second)
import Control.Monad ((<=<))
import Control.Monad.Try (Try, throw, tell, cons)
import Data.Map.Strict (empty)
import Data.Either (partitionEithers)
import Data.List (unfoldr, (\\), nub, partition, sort)
import Data.Bool (bool)
import Data.String (IsString)
import Data.Log (Log, (.+.), intersperse, unwords, log, Loggable(..))
import Data.Derivation.Constraint (
	Constraint,
	vars, has, isDerivFrom, positives, selfContained, eliminate )
import Data.Derivation.Expression.Internal (
	Exp, ExpType(..), constraint, varBool )

---------------------------------------------------------------------------

-- * CAN DERIVE
-- * GIVENS
--	+ NEWTYPE GIVENS AND CONSTRUCTOR
--	+ GIVENS VARIABLES
--	+ REMOVE VARIABLE
-- * WANTED

---------------------------------------------------------------------------
-- CAN DERIVE
---------------------------------------------------------------------------

canDerive :: (IsString s, Ord v) => Givens v -> Wanted v -> Try e (Log s v) Bool
canDerive g = (canDerive1 g `allM`) . unWanted

allM :: Monad m => (a -> m Bool) -> [a] -> m Bool
p `allM` xs = and <$> p `mapM` xs

canDerive1 :: forall s v e .
	(IsString s, Ord v) => Givens v -> Wanted1 v -> Try e (Log s v) Bool
canDerive1 g w = (s || d) <$ if s
	then t $ ttl .+. lw .+. " is self-contained"
	else t $ ttl .+. lw .+. " can" .+. nt .+. " be derived from"
	where
	s = selfContained w
	d = any (w `isDerivFrom`) . unGivens . foldr rmVar g $ gVars g \\ vars w
	t = tell @(Log s v)
	ttl = "canDerive1: "; lw = log w; nt = bool "not" "" d

---------------------------------------------------------------------------
-- GIVENS
---------------------------------------------------------------------------

-- NEWTYPE GIVENS AND CONSTRUCTOR

newtype Givens v = Givens { unGivens :: [Constraint v] } deriving Show

instance IsString s => Loggable s v (Givens v) where
	log (Givens cs) = "(Givens [" .+. intersperse ", " (log <$> cs) .+. "])"

givens :: forall s v . (IsString s, Ord v) =>
	[Exp v 'Boolean] -> Try (Log s v) (Log s v) (Givens v)
givens gs = do
	t $ "givens ([Exp v 'Boolean]): " .+. unwords (log <$> gs)
	gs' <- Givens . nub . sort . ((++) <$> id <*> (positives <$>)) . concat
		<$> (uncurry cons <=< constraint (varBool gs)) `mapM` gs
	gs' <$ t ("givens (Givens v): " .+. log gs')
	where t = tell @(Log s v)

-- GIVENS VARIABLES

gVars :: Ord v => Givens v -> [Maybe v]
gVars = nub . sort . concat . (vars <$>) . unGivens

-- REMOVE VARIABLE

rmVar :: Ord v => Maybe v -> Givens v -> Givens v
rmVar v (Givens gs) = Givens . sort . concat . uncurry (:)
	. second (unfoldUntil null (rvStep v)) $ partition (not . (`has` v)) gs

rvStep :: Ord v => Maybe v -> [Constraint v] -> ([Constraint v], [Constraint v])
rvStep _ [] = ([], [])
rvStep v (c : cs) = partitionEithers $ rmVar1 v c <$> cs

rmVar1 :: Ord v => Maybe v -> Constraint v ->
	Constraint v -> Either (Constraint v) (Constraint v)
rmVar1 v c0 c = maybe (Right c) Left $ eliminate v c0 c

unfoldUntil :: (s -> Bool) -> (s -> (r, s)) -> s -> [r]
unfoldUntil p f = unfoldr $ flip bool Nothing <$> Just . f <*> p

---------------------------------------------------------------------------
-- WANTED
---------------------------------------------------------------------------

newtype Wanted v = Wanted { unWanted :: [Wanted1 v] } deriving Show

type Wanted1 v = Constraint v

instance IsString s => Loggable s v (Wanted v) where
	log (Wanted cs) = "(Wanted [" .+. intersperse ", " (log <$> cs) .+. "])"

wanted :: forall s v . (IsString s, Ord v) =>
	Exp v 'Boolean -> Try (Log s v) (Log s v) (Wanted v)
wanted w = do
	t $ "wanted (Exp v 'Boolean): " .+. log w
	(e, s) <- constraint empty w
	w' <- either throw (pure . Wanted . (: s)) e
	w' <$ t ("wanted (Wanted v): " .+. log w')
	where t = tell @(Log s v)