packages feed

lol-typing-1.20160822: Language/LOL/Typing/Solver/Monad.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Typing.Solver.Monad where

import Control.Applicative (Applicative(..))
import Control.Monad (Monad(..))
import qualified Control.Monad.Classes as MC
import qualified Control.Monad.Classes.Run as MC
import qualified Control.Monad.Trans.State.Lazy as SL
import Data.Bool
import Data.Eq (Eq(..))
import Data.Function (($), (.))
import Data.Functor ((<$>))
import qualified Data.List as List
import Data.Map.Strict (Map)
import Data.Monoid (Monoid(..), (<>))
import Data.Ord (Ord(..), max)
import Data.Sequence (Seq)
import Data.Text.Buildable (Buildable(..))
import Prelude (Num(..))
import Text.Show (Show(..))

import Language.LOL.Typing.Type
import Language.LOL.Typing.Solver.Common
import Language.LOL.Typing.Solver.Constraint
import Language.LOL.Typing.Solver.Monotype
import Language.LOL.Typing.Solver.Polytype
import Language.LOL.Typing.Solver.Class
import Language.LOL.Typing.Lib.Data.Empty (Empty(..))
import qualified Language.LOL.Typing.Lib.Control.Monad.Classes.Instance as MC
import qualified Language.LOL.Typing.Lib.Control.Monad.Classes.StateFix as MC
import qualified Language.LOL.Typing.Lib.Control.Monad.Classes.StateInstance as MC
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build

-- * Type 'Solver'

-- | A 'Monad' stack gathering all the @Solver_*@.
--
-- * @m@ is a base 'Monad'
--   (for instance within which 'Solver_Log's
--   can be handled during the solving).
-- * @info@ is a polymorphic type to hold informations
--   about the origin of the 'Constraint's.
-- * @monotype_substitution@ is a for accumulating known 'Monotype's
--   (for example it can be: 'Substitution_Finite' or 'Substitution_Fixpoint').
type Solver info m monotype_substitution =
	MC.StateLazyFixT State_Constraint
	(SL.StateT (State_Monotype monotype_substitution)
	(SL.StateT (State_Polytype info)
	(SL.StateT (State_Class info)
	(WriterT Solver_Log
	m))))

-- * Type 'Solver_Log'

data Solver_Log
 =   Solver_Log_States [MC.Instance State]
     -- ^ Logs summarizing the 'State's.
 |   Solver_Log_Constraint Log_Constraint
     -- ^ Log of 'Constraint' handling.
 |   Solver_Log_Class Log_Class
     -- ^ Log of 'Class' handling.
 |   Solver_Log_Polytype Log_Polytype
     -- ^ Log of 'Polytype' handling.

instance Monad m
 => Solver_Logable Log_Constraint (Solver info m sub) where
	log = MC.tell . Solver_Log_Constraint
instance Monad m
 => Solver_Logable Log_Class (Solver info m sub) where
	log = MC.tell . Solver_Log_Class
instance Monad m
 => Solver_Logable Log_Polytype (Solver info m sub) where
	log = MC.tell . Solver_Log_Polytype

log_states
 :: forall m.
 ( MC.MonadWriter Solver_Log m
 , MC.MonadStateInstance State m
 , Solver_Constraint m
 ) => m ()
log_states = do
	(states::[MC.Instance State]) <- MC.getInstance
	MC.tell $ Solver_Log_States states

-- | Type synonym to 'MC.CustomWriterT',
-- /eta-reduced/ to fit in 'Solver'.
type WriterT w m = MC.CustomWriterT' w m m

-- * Type 'Solver_Info'

data Solver_Info info
 =   Solver_Info_Class    (Info_Class info)
 |   Solver_Info_Monotype Info_Monotype
 |   Solver_Info_Polytype Info_Polytype
 deriving (Eq, Show)
instance Buildable info => Buildable (Solver_Info info) where
	build x =
		case x of
		 Solver_Info_Class    i -> build i
		 Solver_Info_Monotype i -> build i
		 Solver_Info_Polytype i -> build i
instance Buildable info => Buildable [Solver_Info info] where
	build = Build.list

instance
 ( Monad m
 , Buildable info
 ) =>  Information (Solver info m sub) where
	type Info        (Solver info m sub) = info

data instance Error (Solver info m sub)
 = Solver_Error_Class    Error_Class
 | Solver_Error_Monotype Error_Monotype
 | Solver_Error_Polytype Error_Polytype
 deriving (Eq, Show)

instance
 ( Monad m
 , Buildable info
 ) => Solver_Constraint (Solver info m sub)
instance
 ( Monad m
 , Buildable info
 , Solver_Monotype_Substitution sub
 , Infoable Info_Monotype info
 ) =>  Solver_Monotype     (Solver info m sub) where
	type Solver_Monotype_Sub (Solver info m sub) = sub
	error_monotype = Solver_Error_Monotype
instance
 ( Monad m
 , Buildable info
 , info ~ Info (Solver info m sub)
 , Infoable Info_Polytype info
 ) => Solver_Polytype (Solver info m sub) where
	error_polytype = Solver_Error_Polytype
instance
 ( Monad m
 , Buildable info
 , info ~ Info (Solver info m sub)
 , Infoable Info_Monotype info
 , Infoable Info_Polytype info
 , Infoable (Info_Class info) info
 , Solver_Monotype_Substitution sub
 ) => Solver_Class (Solver info m sub) where
	error_class = Solver_Error_Class

-- | Return within base 'Monad' @m@.
-- the 'Solver_Result' produced
-- by evaluating the given 'Solver',
-- while using the given 'Solver_Log' handler.
solve ::
 ( sol ~ Solver info m sub
 , res ~ Solver_Result (Info sol) (Error sol)
 , Monad m
 , Empty sub
 ) => (Solver_Log -> m ())
 -> sol res -> m res
solve writer_log =
	MC.evalWriterWith writer_log .
	MC.evalStateLazy    empty .
	MC.evalStateLazy    empty .
	MC.evalStateLazy    empty .
	MC.evalStateLazyFix empty

-- | Return the 'Solver' produced
-- by the given 'Solver_Config' and 'Contraint's.
solver ::
 ( sol ~ Solver info m sub
 , res ~ Solver_Result (Info sol) (Error sol)
 , Monad m
 , Solver_Monotype_Substitution sub
 , Solvable constraint sol
 , MC.CansMonadStateInstance State (MC.EffState ()) m ~ '[ 'False ]
   -- NOTE: require that the inner 'Monad' does not have 'State' instances.
 , Buildable info
 , Infoable Info_Monotype info
 , Infoable Info_Polytype info
 , Infoable (Info_Class info) info
 ) => Solver_Config -> Seq constraint -> sol res
solver cfg cs = do
	solver_init cs cfg
	solver_run cs
	solver_result

-- | Run the solvers of a 'Monad' over the given 'Constraint's.
solver_run ::
 ( Solvable constraint m
 , Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , Solver_Class m
 , MC.MonadWriter Solver_Log m
 , MC.MonadStateInstance State m
 ) => Seq constraint -> m ()
solver_run cs = do
	constraint_push_many $ constraints cs
	log_states
	constraint_solve
	monotype_substitution_consistentify
	polytype_rigids_check
	class_ambiguities
	log_states

-- ** Type 'Solver_Config'

data Solver_Config
 =   Solver_Config
 {   solver_config_class_env :: Class_Env
 ,   solver_config_freshvar  :: Freshvar
 ,   solver_config_synotypes :: Synotype_Substitution
 ,   solver_config_check     :: Bool
 ,   solver_config_stop      :: Bool
 }

-- | Return a 'Solver_Config'.
solver_config :: Solver_Config
solver_config =
	Solver_Config
	 { solver_config_class_env = class_env_default
	 , solver_config_freshvar  = -1
	 , solver_config_synotypes = empty
	 , solver_config_check     = option_current $ state_constraint_option_check empty
	 , solver_config_stop      = option_current $ state_constraint_option_stop  empty
	 }

-- | Initialize a solver 'Monad' with given 'Solver_Config'.
solver_init ::
 forall m constraints.
 ( Substitutable constraints
 , Solver_Constraint m
 , Solver_Polytype m
 , Solver_Class m
 ) => constraints -> Solver_Config -> m ()
solver_init cs cfg = do
	polytype_freshvar_set freshvar
	synotype_substitution_set $
		solver_config_synotypes cfg
	class_env_set $
		solver_config_class_env cfg
	MC.modifyFix $ \(s::State_Constraint m) ->
		s{ state_constraint_option_check =
			(state_constraint_option_check s)
			 { option_current = solver_config_check cfg }
		 , state_constraint_option_stop =
			(state_constraint_option_stop s)
			 { option_current = solver_config_stop cfg }
		 }
	where
	freshvar
	 | solver_config_freshvar cfg < 0 = 1 + List.maximum (-1 : subvars cs)
	 | otherwise                      = solver_config_freshvar cfg

-- ** Type 'Solver_Result'

data Solver_Result info err
 =   Solver_Result
 {   solver_result_freshvar    :: Freshvar
 ,   solver_result_monotypes   :: Substitution_Fixpoint
 ,   solver_result_polytypes   :: Polysub
 ,   solver_result_quantifiers :: Map Monovar [Quantifier]
 ,   solver_result_qualifiers  :: [Class_Qualifier]
     -- ^ 'Class_Qualifier's assumed to hold.
 ,   solver_result_errors      :: [(info, err)]
 }

instance Empty (Solver_Result info err) where
	empty = mempty
instance Monoid (Solver_Result info err) where
	mempty = Solver_Result 0 mempty mempty mempty mempty mempty
	mappend
	 (Solver_Result fr1 ms1 ps1 qts1 qls1 es1)
	 (Solver_Result fr2 ms2 ps2 qts2 qls2 es2) =
		Solver_Result
		 (fr1 `max` fr2)
		 (ms1 <> ms2)
		 (ps1 <> ps2)
		 (qts1 <> qts2)
		 (qls1 <> qls2)
		 (es1 <> es2)

-- | Return a 'Solver_Result' from a solver 'Monad'.
solver_result ::
 ( Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , Solver_Class m
 ) => m (Solver_Result (Info m) (Error m))
solver_result =
	Solver_Result
	 <$> polytype_freshvar
	 <*> monotype_substitution
	 <*> polytype_substitution
	 <*> polytype_quantifiers
	 <*> class_qualifiers_reduced
	 <*> constraint_errors