packages feed

lol-typing-1.20160822: Language/LOL/Typing/Type/Unification.hs

{-# LANGUAGE NamedFieldPuns #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Typing.Type.Unification where

import Data.Bool
import Data.Either (Either(..))
import Data.Eq (Eq(..))
import Data.Function (($))
import Data.Functor ((<$>))
import qualified Data.List as List
import qualified Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe)
import Data.Monoid (Monoid(..), (<>))
import Data.Tuple (snd)
import Prelude (error)
import Text.Show (Show(..))

import Language.LOL.Typing.Type.Monotype
import Language.LOL.Typing.Type.Substitution
import Language.LOL.Typing.Type.Synotype
import Language.LOL.Typing.Lib.Data.Empty (Empty(..))

-- * Type 'Unification'

type Unification
 =   Substitution_Finite

-- | Like 'mgu_with_synotypes', but with an empty 'Synotype_Substitution'.
mgu :: Monotype -> Monotype -> Either Unification_Error Unification
mgu t1 t2 = snd <$> mgu_with_synotypes empty t1 t2

-- | The /most general unification/ ('Substitution') of two 'Monotype's,
--   expanding 'Synotypes' as lazy as possible.
--
-- Example:
--
-- @
--   if String => [Char]
--   then   v11 -> [v11]  `mgu`  String -> [[v14]]
--   should be:
--      [ v11 := [Char] , v14 := Char ]
-- @
--
-- NOTE: the boolean indicates whether expansions were necessary.
mgu_with_synotypes
 :: Synotype_Substitution
 -> Monotype -> Monotype
 -> Either Unification_Error (Bool, Unification)
mgu_with_synotypes syns = go mempty
	where
	err s = error $ "mgu_with_synotypes: " <> s
	go sub t1 t2 =
		case (app_spine_left t1, app_spine_left t2) of
		 (App_Spine (Monotype_Var v) [], _) -> go_Var sub v t2
		 (_, App_Spine (Monotype_Var v) []) -> go_Var sub v t1
		 (  App_Spine (Monotype_Const c1) s1
		  , App_Spine (Monotype_Const c2) s2 )
		  | c1 == c2 && not (is_panthom_synotype syns c1) ->
			go_List sub s1 s2
		  | otherwise ->
			case synexpand_top_step_ordered syns (t1, t2) of
			 Nothing -> Left $ Unification_Error_Constant_clash c1 c2
			 Just (t1', t2') ->
				case go sub t1' t2' of
				 Left e -> Left e
				 Right (_, sub') -> Right (True, sub')
		 _ -> case (t1, t2) of
			 (Monotype_App l1 r1, Monotype_App l2 r2) ->
				go_List sub [l1, r1] [l2, r2]
			 _ ->  err "illegal type"
	go_Var sub v ty =
		case Map.lookup v sub of
		 Just t2 ->
			case go sub ty t2 of
			 Right (True, sub') ->
				let mgt =
					fromMaybe (err "illegal types") $
					mgt_with_synotypes syns
					 (sub' `substitute` ty)
					 (sub' `substitute` t2) in
				Right
				 ( True
				 , substitution_finite_union
					 (substitution_finite_singleton v mgt)
					 (substitution_domain_remove [v] sub')
				 )
			 e -> e
		 Nothing ->
			case sub `substitute` ty of
			 Monotype_Var v' | v == v' -> Right (False, sub)
			 ty' | v `List.elem` subvars ty' -> Left (Unification_Error_Infinite_type v)
			     | otherwise -> Right (False, substitution_finite_singleton v ty' `substitution_finite_union` sub)
	go_List sub [] [] = Right (False, sub)
	go_List sub (s:ss) (t:tt) =
		case go sub s t of
		 Left e -> Left e
		 Right (b,sub') ->
			case go_List sub' ss tt of
			 Left e -> Left e
			 Right (b', sub'') -> Right (b || b', sub'')
	go_List _ _ _ = err "kinds do not match"

-- | Given a set of 'Synotype_Substitution', can two types be unified?
unifiable :: Synotype_Substitution -> Monotype -> Monotype -> Bool
unifiable syns t1 t2 =
	case mgu_with_synotypes syns t1 t2 of
	 Left  _ -> False
	 Right _ -> True

-- | Same as unifiable, but takes as input a list of 'Monotype's
unifiables :: Synotype_Substitution -> [Monotype] -> Bool
unifiables syns (t1:t2:ts) =
	case mgu_with_synotypes syns t1 t2 of
	 Left _         -> False
	 Right (_, sub) -> unifiables syns (sub `substitute` (t2:ts))
unifiables _ _ = True

-- ** Type 'Unification_Error'

-- | Reasons why two 'Monotype's cannot be unified.
data Unification_Error
 =   Unification_Error_Constant_clash Monoconst Monoconst
     -- ^ Two different 'Monoconst's clash (they should be the same).
 |   Unification_Error_Infinite_type Monovar
     -- ^ A 'Monotype_Var' is unified with a composed 'Monotype'
     --   which contains this same 'Monotype_Var'.
 deriving (Eq, Show)