packages feed

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

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
-- | Type inference for /monomorphic types/.
module Language.LOL.Typing.Solver.Monotype where

import Control.Monad (Monad(..), mapM)
import qualified Control.Monad.Classes as MC
import Data.Bool
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import Data.Function (($), (.), id)
import Data.Int (Int)
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Monoid (Monoid(..), (<>))
import Data.Text (Text)
import Data.Text.Buildable (Buildable(..))
import Data.Tuple (uncurry)
import Prelude (error)
import Text.Show (Show(..))

import Language.LOL.Typing.Type
import Language.LOL.Typing.Solver.Common
import Language.LOL.Typing.Solver.Constraint
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_Monotype'

data State_Monotype sub
 =   State_Monotype
 {   state_monotype_substitution :: sub
     -- ^ Known 'Monovar's.
 ,   state_monotype_synotypes :: Synotype_Substitution
     -- ^ Known 'Synotype's.
 } deriving (Show)

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

instance Buildable sub => Buildable (State_Monotype sub) where
	build State_Monotype
	 { state_monotype_substitution = sub
	 , state_monotype_synotypes    = synotys
	 } =
		Build.unlines
		 [ "monotypes: "
		 , Build.indent "  " sub
		 , "synotypes: " <> build synotys
		 ]
instance Buildable sub => State (State_Monotype sub) where
	state_name _ = "State_Monotype"
	state_show = Build.text
instance Empty sub => Empty (State_Monotype sub) where
	empty = State_Monotype
		 { state_monotype_substitution = empty
		 , state_monotype_synotypes    = empty
		 }

-- * Class 'Solver_Monotype'

-- | A type class for 'Monad's containing a 'Substitution',
-- that may be in an inconsistent state.
class
 ( Solver_Constraint m
 , Monad m
 , MC.MonadState (State_Monotype (Solver_Monotype_Sub m)) m
 , Solver_Monotype_Substitution (Solver_Monotype_Sub m)
 , Infoable Info_Monotype (Info m)
 ) => Solver_Monotype (m :: * -> *) where
	error_monotype :: Error_Monotype -> Error m
	type Solver_Monotype_Sub m
	
	monotype_substitution :: m Substitution_Fixpoint
	monotype_substitution = MC.gets $
	 \(s::State_Monotype (Solver_Monotype_Sub m)) ->
		monotype_substitution_fixpoint $
		state_monotype_substitution s
	
	-- | Lookup the value of a 'Monotype_Var' in the substitution.
	monotype_lookup :: Monovar -> m Monotype
	monotype_lookup v = MC.gets $
	 \(s::State_Monotype (Solver_Monotype_Sub m)) ->
		fromMaybe (Monotype_Var v) $
		substitution_lookup v $
		state_monotype_substitution s
	
	-- | Apply the 'Substitution' to any instance of 'Substitutable'.
	monotype_substitute :: Substitutable a => a -> m a
	monotype_substitute a = do
		let subvars_a = subvars a
		monotys <- monotype_lookup `mapM` subvars_a
		let sub = substitution_finite (List.zip subvars_a monotys)
		return (sub `substitute` a)
	
	-- | Unify two 'Monotype's.
	--
	-- NOTE: In the end, the two 'Monotype's should be equivalent
	-- under 'monotype_substitution'. However, unifying two 'Monotype's
	-- may bring the substitution into an /inconsistent state/.
	monotype_unify :: Info m -> Monotype -> Monotype -> m ()
	monotype_unify info ty1 ty2 = do
		synotys <- synotype_substitution
		subty1 <- monotype_substitute ty1
		subty2 <- monotype_substitute ty2
		case monotype_substitution_unify
		 synotys (ty1, subty1) (ty2, subty2) of
		 Left err ->
			constraint_error_insert
			 (error_monotype $ Error_Monotype_Unification err)
			 info
		 Right (_log, uni) -> -- do
			-- constraint_log "monotype_unify" log
			MC.modify $ \(s::State_Monotype (Solver_Monotype_Sub m)) ->
				s{state_monotype_substitution =
					uni $ state_monotype_substitution s }
	-- | Make 'state_monotype_substitution' consistent.
	--
	-- NOTE: Only relevant for 'Substitution' states
	-- that can be inconsistent
	-- (eg. the /type graph/ substitution state).
	monotype_substitution_consistentify :: m ()
	monotype_substitution_consistentify = return ()
	
	synotype_substitution :: m Synotype_Substitution
	synotype_substitution = MC.gets $
	 \(s::State_Monotype (Solver_Monotype_Sub m)) ->
		state_monotype_synotypes s
	synotype_substitution_set :: Synotype_Substitution -> m ()
	synotype_substitution_set state_monotype_synotypes =
		MC.modify $ \(s::State_Monotype (Solver_Monotype_Sub m)) ->
			s{ state_monotype_synotypes }

-- ** Class 'Solver_Monotype_Substitution'

class
 ( Buildable sub
 , Show sub
 , Monoid sub
 , Substitution sub
 ) => Solver_Monotype_Substitution sub where
	monotype_substitution_fixpoint :: sub -> Substitution_Fixpoint
	monotype_substitution_unify
	 :: Synotype_Substitution
	 -> (Monotype, Monotype)
	 -> (Monotype, Monotype)
	 -> Either Unification_Error (Text, sub -> sub)

instance Solver_Monotype_Substitution Substitution_Finite where
	monotype_substitution_fixpoint = Substitution_Fixpoint
	monotype_substitution_unify synotys (_origty1, ty1) (_origty2, ty2) =
		case mgu_with_synotypes synotys ty1 ty2 of
		 Left err -> Left err
		 Right (_, uni) -> Right $ (Build.text uni,) $ substitution_finite_union uni
instance Solver_Monotype_Substitution Substitution_Fixpoint where
	monotype_substitution_fixpoint = id
	monotype_substitution_unify synotys (origty1, ty1) (origty2, ty2) =
		case mgu_with_synotypes synotys ty1 ty2 of
		 Left err -> Left err
		 Right (was_synexpanded, uni) ->
			Right $ (Build.text uni,) $
				(if was_synexpanded then subexpand else id) .
				subunion uni
			where
			subunion x (Substitution_Fixpoint y) =
				Substitution_Fixpoint $
				(`Map.union` y) $
				Map.fromList
				 [ (v, fromMaybe (Monotype_Var v) $ substitution_lookup v x)
				 | v <- substitution_domain x
				 ]
			subexpand =
				substitution_fixpoint_synexpand synotys origty2 unity .
				substitution_fixpoint_synexpand synotys origty1 unity
			unity =
				fromMaybe (error "monotype_substitution_unify: types not unifiable") $
				mgt_with_synotypes synotys
				 (uni `substitute` ty1)
				 (uni `substitute` ty2)

-- TODO: understand and test this code better…

-- NOTE: Top: The key idea is as follows:
-- try to minimize the number of expansions by 'Synotype's.
-- If a type is expanded, then this should be recorded in the substitution.
-- Invariant of this function should be that "atp" (the first type) can be
-- made equal to "utp" (the second type) with a number of 'Synotype' expansions
substitution_fixpoint_synexpand
 :: Synotype_Substitution
 -> Monotype
 -> Monotype
 -> Substitution_Fixpoint
 -> Substitution_Fixpoint
substitution_fixpoint_synexpand syns =
	go
	where
	go :: Monotype -> Monotype -> Substitution_Fixpoint -> Substitution_Fixpoint
	go atp utp original =
		case (app_spine_left atp, app_spine_left utp) of
		 (App_Spine (Monotype_Var i) [], _) -> writeIntType i utp original
		 (  App_Spine (Monotype_Const s) as
		  , App_Spine (Monotype_Const t) bs
		  ) | s == t && not (is_panthom_synotype syns s) ->
			List.foldr (uncurry go) original (List.zip as bs)
		 (App_Spine (Monotype_Const _) _, _) ->
			case synexpand_top_step (synotypes syns) atp of
			 Just atp' -> go atp' utp original
			 Nothing -> error $ "substitution_fixpoint_synexpand: inconsistent types(1)" <> show (atp, utp)
		 _ -> error $ "substitution_fixpoint_synexpand: inconsistent types(2)" <> show (atp, utp)
	
	writeIntType :: Int -> Monotype -> Substitution_Fixpoint -> Substitution_Fixpoint
	writeIntType i utp original@(Substitution_Fixpoint fm) =
		case Map.lookup i fm of
		 Nothing ->
			case utp of
			 Monotype_Var j | i == j -> original
			 _ -> Substitution_Fixpoint (Map.insert i utp fm)
		 Just atp ->
			case (app_spine_left atp, app_spine_left utp) of
			 (App_Spine (Monotype_Var j) [], _) -> writeIntType j utp original
			 (  App_Spine (Monotype_Const s) as
			  , App_Spine (Monotype_Const t) bs
			  ) | s == t ->
				List.foldr (uncurry go) original (List.zip as bs)
			 (App_Spine (Monotype_Const _) _, _) ->
				case synexpand_top_step (synotypes syns) atp of
				 Just atp' -> writeIntType i utp (Substitution_Fixpoint (Map.insert i atp' fm))
				 Nothing ->
					-- NOTE: Top: FIX!!!   HERSCHRIJVEN!
					-- de volgende situatie trad op:
					--    utp=Categorie, atp = [Char]
					--  met type Categorie = String
					case synexpand_top_step (synotypes syns) utp of
					 Just utp' -> writeIntType i atp (Substitution_Fixpoint (Map.insert i utp' fm))
					 Nothing -> error $ "substitution_fixpoint_synexpand: inconsistent types(1)" <> show (i, utp, atp)
			 _ -> error "substitution_fixpoint_synexpand: inconsistent types(2)"

-- ** Class 'Info_Monotype'

data Info_Monotype
 =   Info_Monotype_Unification Monotype Monotype
 deriving (Eq, Show)

instance Buildable Info_Monotype where
	build x =
		case x of
		 Info_Monotype_Unification m1 m2 ->
			"Info_Monotype_Unification " <> Build.tuple [m1, m2]

-- ** Type 'Error_Monotype'

data Error_Monotype
 =   Error_Monotype_Unification Unification_Error
 deriving (Eq, Show)