packages feed

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

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
-- | Type inference for /type class polymorphism/
-- (aka. /parametric overloading/).
module Language.LOL.Typing.Solver.Class where

import Control.Monad (Monad(..), forM, forM_, mapM)
import qualified Control.Monad.Classes as MC
import Data.Bool
import Data.Eq (Eq(..))
import qualified Data.Foldable as Foldable
import Data.Function (($), (.))
import Data.Functor ((<$>))
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..), isJust)
import Data.Monoid (Monoid(..), (<>))
import Data.Text (Text)
import Data.Text.Buildable (Buildable(..))
import Data.Tuple (fst)
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 qualified Language.LOL.Typing.Lib.Control.Monad.Classes.Instance as MC
import Language.LOL.Typing.Lib.Data.Empty (Empty(..))
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build

-- * Type 'State_Class'

data State_Class info
 =   State_Class
 {   state_class_directives :: [Class_Directive info]
     -- ^ 'Class_Directive's for 'state_class_qualifiers'.
 ,   state_class_env :: Class_Env
     -- ^ Known 'Class'es and 'Class_Instance's.
 ,   state_class_qualifiers :: State_Class_Qualifiers info
     -- ^ 'Class' assertions.
 } deriving (Show)

-- | Make 'State_Class' collectable as a 'State' instance
-- out of a 'Monad' stack.
type instance MC.Class State (State_Class info) = 'True

instance Empty (State_Class info) where
	empty = State_Class
	 { state_class_directives = mempty
	 , state_class_env        = mempty
	 , state_class_qualifiers = empty
	 }
instance Buildable info => State (State_Class info) where
	state_name _ = "State_Class"
	state_show = Build.text
instance Buildable info => Buildable (State_Class info) where
	build State_Class
	 { state_class_directives
	 , state_class_env
	 , state_class_qualifiers
	 } =
		mconcat $ List.intersperse "\n"
		 [ "class directives: "  <> Build.list state_class_directives
		 , "class environment: " <> Build.list (Map.keys state_class_env)
		 , "class qualifiers: "
		 , Build.indent "  " state_class_qualifiers
		 ]

-- ** Type 'State_Class_Qualifiers'

data State_Class_Qualifiers info
 =   State_Class_Qualifiers
 {   state_class_qualifiers_assumed     :: [Infoed info Class_Qualifier]
 ,   state_class_qualifiers_generalized :: [Infoed info Class_Qualifier]
 ,   state_class_qualifiers_toprove     :: [Infoed info Class_Qualifier]
     -- ^ Once the constraints have been solved, this list should be empty.
 } deriving (Show)
 
instance Empty (State_Class_Qualifiers info) where
	empty =
		State_Class_Qualifiers
		 { state_class_qualifiers_assumed     = []
		 , state_class_qualifiers_generalized = []
		 , state_class_qualifiers_toprove     = []
		 }
instance Buildable info => Buildable (State_Class_Qualifiers info) where
	build State_Class_Qualifiers
	 { state_class_qualifiers_assumed
	 , state_class_qualifiers_generalized
	 , state_class_qualifiers_toprove
	 } =
		Build.unlines
		 [ "assumed: "     <> Build.list state_class_qualifiers_assumed
		 , "generalized: " <> Build.list state_class_qualifiers_generalized
		 , "toprove: "     <> Build.list state_class_qualifiers_toprove
		 ]
instance Substitutable (State_Class_Qualifiers info) where
	subvars (State_Class_Qualifiers qs gs as) = subvars (infoed <$> (qs <> gs <> as))
	sub `substitute` State_Class_Qualifiers
	 { state_class_qualifiers_toprove     = ps
	 , state_class_qualifiers_generalized = gs
	 , state_class_qualifiers_assumed     = as
	 } =
		State_Class_Qualifiers
		 { state_class_qualifiers_toprove     = ((sub `substitute`) <$>) <$> ps
		 , state_class_qualifiers_generalized = ((sub `substitute`) <$>) <$> gs
		 , state_class_qualifiers_assumed     = ((sub `substitute`) <$>) <$> as
		 }

-- * Class 'Solver_Class'

class
 ( Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , MC.MonadState (State_Class (Info m)) m
 , Infoable (Info_Class (Info m)) (Info m)
 , Solver_Logable Log_Class m
 ) => Solver_Class m where
	error_class :: Error_Class -> Error m
	
	class_qualifiers :: m (State_Class_Qualifiers (Info m))
	class_qualifiers =
		MC.gets $ \(s::State_Class (Info m)) ->
			state_class_qualifiers s
	class_qualifiers_modify
	 :: (State_Class_Qualifiers (Info m) -> State_Class_Qualifiers (Info m))
	 -> m ()
	class_qualifiers_modify f =
		MC.modify $ \(s::State_Class (Info m)) ->
			s{ state_class_qualifiers =
				f (state_class_qualifiers s) }
	class_qualifier_toprove
	 :: Info m -> Class_Qualifier -> m ()
	class_qualifier_toprove info q = do
		log $ Log_Class_Qualifier_ToProve q
		class_qualifiers_modify $ \quals ->
			quals{ state_class_qualifiers_toprove =
				Infoed info q : state_class_qualifiers_toprove quals }
	class_qualifier_assume
	 :: Info m -> Class_Qualifier -> m ()
	class_qualifier_assume info q = do
		log $ Log_Class_Qualifier_Assume q
		class_qualifiers_modify $ \quals ->
			quals{ state_class_qualifiers_assumed =
				Infoed info q : state_class_qualifiers_assumed quals }
	class_qualifiers_map
	 :: (Class_Qualifier -> m Class_Qualifier) -> m ()
	class_qualifiers_map f = do
		let g = mapM $ \(Infoed info q) ->
			f q >>= \new -> return (Infoed info new)
		state_class_qualifiers_toprove <-
			(>>= g) $
			MC.gets $ \(s::(State_Class (Info m))) ->
				state_class_qualifiers_toprove $
				state_class_qualifiers s
		state_class_qualifiers_generalized <-
			(>>= g) $
			MC.gets $ \(s::(State_Class (Info m))) ->
				state_class_qualifiers_generalized $
				state_class_qualifiers s
		state_class_qualifiers_assumed <-
			(>>= g) $
			MC.gets $ \(s::(State_Class (Info m))) ->
				state_class_qualifiers_assumed $
				state_class_qualifiers s
		class_qualifiers_modify $ \quals ->
			quals
			 { state_class_qualifiers_toprove
			 , state_class_qualifiers_generalized
			 , state_class_qualifiers_assumed
			 }
	
	class_qualifiers_reduced :: m [Class_Qualifier]
	class_qualifiers_reduced = do
		synotys <- synotype_substitution
		clenv <- class_env
		quals <- class_qualifiers
		return $ fst $
			class_context_reduction
			 synotys clenv $ infoed <$> (
				state_class_qualifiers_toprove quals <>
				state_class_qualifiers_generalized quals <>
				state_class_qualifiers_assumed quals
			 )
	
	-- | Generalize a 'Monotype', preserving given 'Rigtype's,
	-- and such that the resulting 'Polytype'
	-- has all the 'Class_Qualifier's from 'state_class_qualifiers_toprove',
	-- when they apply at least on one of the 'quantifiers' of this 'Polytype'.
	class_polytype_forall
	 :: [Rigtype] -> Monotype -> m Polytype
	class_polytype_forall rigtys monoty = do
		State_Class_Qualifiers
		 { state_class_qualifiers_toprove     = quals_toprove
		 , state_class_qualifiers_generalized = quals_generalized
		 } <- class_qualifiers
		let rigvas = subvars rigtys
		let polyvars = subvars monoty List.\\ rigvas
		let has_polyvars = Foldable.any (`List.elem` polyvars) . subvars . infoed
		let (  quals_toprove_poly
		     , quals_toprove_mono
		     ) = List.partition has_polyvars quals_toprove
		let quals_generalized_poly = List.filter has_polyvars quals_generalized
		class_qualifiers_modify $ \quals ->
			quals
			 { state_class_qualifiers_toprove     = quals_toprove_mono
			 , state_class_qualifiers_generalized = quals_toprove_poly <> state_class_qualifiers_generalized quals
			 }
		return $
			forall_but rigvas $
			Qualification
			 (infoed <$> (quals_toprove_poly <> quals_generalized_poly))
			 monoty
	
	class_improve
	 :: Bool -> m [(Info m, Monotype, Monotype)]
	class_improve normal =
		if normal
		then class_improve_normal
		else class_improve_final
	class_improve_normal
	 :: m [(Info m, Monotype, Monotype)]
	class_improve_normal =
		return []
	class_improve_final
	 :: m [(Info m, Monotype, Monotype)]
	class_improve_final =
		return []
	
	class_simplify :: m ()
	class_simplify = do
		State_Class_Qualifiers
		 { state_class_qualifiers_toprove
		 , state_class_qualifiers_assumed
		 } <- class_qualifiers
		synotys <- synotype_substitution
		clenv <- class_env
		cldirs <- MC.gets state_class_directives
		clquals <- simplify synotys clenv cldirs
			 state_class_qualifiers_toprove
		class_qualifiers_modify $ \quals ->
			quals{state_class_qualifiers_toprove =
				List.filter (not . class_entails synotys clenv
				 (infoed <$> state_class_qualifiers_assumed) . infoed) clquals }
		where
		simplify ::
		 ( Solver_Constraint m
		 , Solver_Polytype m
		 ) => Synotype_Substitution
		 -> Class_Env
		 -> [Class_Directive (Info m)]
		 ->   [Infoed (Info m) Class_Qualifier]
		 -> m [Infoed (Info m) Class_Qualifier]
		simplify synotys clenv cldirs clquals = do
			hnf' <- go_insts clquals
			are_disjoints (go_super_class [] hnf')
			where
			go_insts
			 ::   [Infoed (Info m) Class_Qualifier]
			 -> m [Infoed (Info m) Class_Qualifier]
			go_insts ts =
				(List.concat <$>) <$>
				forM ts $
				 \q@(Infoed info clqual@(Class_Qualifier clname _)) ->
					if is_class_qualifier_normalized clqual
					then return [q]
					else case class_env_instance_context synotys clenv clqual of
					 Just inst_context ->
						go_insts $
						 Infoed (info_insert
							 (Info_Class_Qualifier_Parent clqual::Info_Class (Info m))
							 info) <$>
						 inst_context
					 Nothing -> do
						constraint_error_insert
						 (error_class Error_Class_Qualifier_Unresolved) $
						 (case cldirs_never of
							 clql:_ -> info_insert $ Info_Class_Directive_Never clql
							 [] -> case infos_cldirs_close of
								 [i] -> info_insert (Info_Class_Directive_Close (Infoed i clname)::Info_Class (Info m))
								 _   -> info_insert (Info_Class_Qualifier_Unresolved clqual::Info_Class (Info m)))
						 info
						return []
						where
						cldirs_never =
							[ Infoed i clql
							| Class_Directive_Never clql i <- cldirs
							, isJust $ class_qualifier_unification synotys clqual clql
							]
						infos_cldirs_close =
							[ i
							| Class_Directive_Close n i <- cldirs
							, n == clname ]
			
			go_super_class
			 :: [Infoed (Info m) Class_Qualifier]
			 -> [Infoed (Info m) Class_Qualifier]
			 -> [Infoed (Info m) Class_Qualifier]
			go_super_class rs [] = rs
			go_super_class rs (x:xs)
			 | class_entails_super_class clenv
				 (infoed <$> (rs <> xs)) (infoed x) = go_super_class rs xs
			 | otherwise = go_super_class (x:rs) xs
			
			are_disjoints
			 ::   [Infoed (Info m) Class_Qualifier]
			 -> m [Infoed (Info m) Class_Qualifier]
			are_disjoints [] = return []
			are_disjoints (t@(Infoed info (Class_Qualifier className ty)):ts) = do
				let f t'@(Infoed info' (Class_Qualifier className' ty')) =
					case
					 [ i
					 | ty == ty'
					 , Class_Directive_Disjoint ss i <- cldirs
					 , className  `List.elem` ss
					 , className' `List.elem` ss
					 ] of
					 [] -> return ([t'], True)
					 info_directive : _ -> do
						constraint_error_insert
						 (error_class Error_Class_Qualifier_Disjoint) $
							info_insert
							 (Info_Class_Directive_Disjoint
								 (Infoed info className)
								 (Infoed info' className'))
							 info_directive
						return ([], False)
				result <- mapM f ts
				let (list, bs) = List.unzip result
				rest <- are_disjoints (List.concat list)
				return $ if Foldable.and bs then t : rest else rest
	
	class_ambiguous :: m ()
	class_ambiguous = do
		State_Class_Qualifiers
		 { state_class_qualifiers_toprove
		 } <- class_qualifiers
		rigvars_rigtypes <- polytype_rigids
		forM_ state_class_qualifiers_toprove $ \q_toprove ->
			case q_toprove of
			 Infoed _ Class_Qualifier{class_qualifier_type=Monotype_Var v} ->
				case
				 [ info
				 | Infoed info (rigvas, _) <- rigvars_rigtypes
				 , v `List.elem` rigvas
				 ] of
				 info:_ -> err_missing q_toprove info
				 _      -> err_ambiguous q_toprove
			 _ -> err_ambiguous q_toprove
		where
		err_ambiguous (Infoed info p) =
			constraint_error_insert
			 (error_class Error_Class_Qualifier_Ambiguous) $
			info_insert
			 (Info_Class_Qualifier_Ambiguous p::Info_Class (Info m)) info
		err_missing q_toprove info =
			constraint_error_insert
			 (error_class Error_Class_Qualifier_Missing) $
			info_insert
			 (Info_Class_Qualifier_Arising_from q_toprove) info
	
	class_env :: m Class_Env
	class_env =
		MC.gets $ \(s::State_Class (Info m)) ->
			state_class_env s
	class_env_set :: Class_Env -> m ()
	class_env_set state_class_env =
		MC.modify $ \(s::State_Class (Info m)) ->
			s{ state_class_env }

-- | When all 'state_constraint_constraints' have been handled,
-- takes all the remaining 'state_class_qualifiers_toprove',
-- and report them as ambiguities.
class_ambiguities ::
 ( Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , Solver_Class m
 ) => m ()
class_ambiguities = do
	class_reduction
	class_improve_fix False
	class_ambiguous

-- | Perform context reduction on the 'state_class_qualifiers_toprove',
-- and removes the 'Class_Qualifier's
-- which are entailed by 'state_class_qualifiers_assumed'.
class_reduction ::
 ( Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , Solver_Class m
 ) => m ()
class_reduction = do
	monotype_substitution_consistentify
	class_qualifiers_map monotype_substitute
	class_improve_fix True
	class_simplify

class_improve_fix ::
 ( Solver_Constraint m
 , Solver_Monotype m
 , Solver_Polytype m
 , Solver_Class m
 ) => Bool -> m ()
class_improve_fix normal = do
	improvements <- class_improve normal
	case improvements of
	 [] -> return ()
	 _  -> do
		forM_ improvements
		 (\(info, t1, t2) -> monotype_unify info t1 t2)
		monotype_substitution_consistentify
		class_improve_fix normal

-- * Class 'Info_Class'

data Info_Class info
 =   Info_Class_Qualifier_Ambiguous Class_Qualifier
 |   Info_Class_Qualifier_Arising_from (Infoed info Class_Qualifier)
 |   Info_Class_Qualifier_Parent Class_Qualifier
 |   Info_Class_Qualifier_Unresolved Class_Qualifier
 |   Info_Class_Directive_Close (Infoed info Text)
 |   Info_Class_Directive_Disjoint (Infoed info Text) (Infoed info Text)
 |   Info_Class_Directive_Never (Infoed info Class_Qualifier)
 deriving (Eq, Show)

instance Buildable info => Buildable (Info_Class info) where
	build x =
		case x of
		 Info_Class_Qualifier_Ambiguous q ->
			"Info_Class_Qualifier_Ambiguous " <> build q
		 Info_Class_Qualifier_Arising_from q ->
			"Info_Class_Qualifier_Arising_from " <> build q
		 Info_Class_Qualifier_Parent q ->
			"Info_Class_Qualifier_Parent " <> build q
		 Info_Class_Qualifier_Unresolved q ->
			"Info_Class_Qualifier_Unresolved " <> build q
		 Info_Class_Directive_Close n ->
			"Info_Class_Directive_Close " <> build n
		 Info_Class_Directive_Disjoint n1 n2 ->
			"Info_Class_Directive_Disjoint " <> build n1 <> " " <> build n2
		 Info_Class_Directive_Never q ->
			"Info_Class_Directive_Never " <> build q

-- * Type 'Error_Class'

data Error_Class
 =   Error_Class_Qualifier_Ambiguous
     -- ^ Example: @forall a. Eq a => Int -> Int@
 |   Error_Class_Qualifier_Disjoint
 |   Error_Class_Qualifier_Missing
 |   Error_Class_Qualifier_Unresolved
     -- ^ A 'Monotype' is missing a required 'Class_Qualifier'.
 deriving (Eq, Show)

-- ** Type 'Log_Class'

data Log_Class
 =   Log_Class_Qualifier_Assume  Class_Qualifier
 |   Log_Class_Qualifier_ToProve Class_Qualifier
 deriving (Show)
instance Buildable Log_Class where
	build x =
		case x of
		 Log_Class_Qualifier_Assume  q -> "class_qualifier_assume  : " <> build q
		 Log_Class_Qualifier_ToProve q -> "class_qualifier_toprove : " <> build q