lol-typing-1.20160822: Language/LOL/Typing/Type/Substitution.hs
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# OPTIONS_GHC -fno-warn-tabs #-}
module Language.LOL.Typing.Type.Substitution where
import Data.Bool
import Data.Either (Either(..), either)
import Data.Eq (Eq(..))
import Data.Function (($), (.))
import Data.Functor ((<$>))
import qualified Data.List as List
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe, maybe)
import Data.Monoid (Monoid(..), (<>))
import Data.Sequence (Seq)
import qualified Data.Set as Set
import Data.Text.Buildable (Buildable(..))
import Prelude (Num(..), error)
import Text.Show (Show(..))
import Language.LOL.Typing.Type.Monotype
import Language.LOL.Typing.Lib.Data.Empty (Empty(..))
-- * Class 'Substitution'
-- | A /monomorphic type substitution/.
--
-- TODO: maybe rename to Monosub?
class Substitution s where
substitution_codomain :: s -> [Monotype]
substitution_domain :: s -> [Monovar]
substitution_domain_remove :: [Monovar] -> s -> s
substitution_domain_restrict :: [Monovar] -> s -> s
substitution_lookup :: Monovar -> s -> Maybe Monotype
-- ** Type 'Substitution_Finite'
-- | A 'Substitution' represented by a finite map.
type Substitution_Finite = Map Monovar Monotype
instance Empty Substitution_Finite where
empty = mempty
instance Buildable Substitution_Finite where
build =
mconcat . List.intersperse "\n" .
Map.foldrWithKey (\var ty ->
((build (Monotype_Var var) <> " == " <> build ty) :)
) []
instance Substitution Substitution_Finite where
substitution_codomain = Map.elems
substitution_domain = Map.keys
substitution_domain_remove vars s =
List.foldr Map.delete s vars
substitution_domain_restrict vars =
Map.filterWithKey (\v _ -> Set.member v vars_kept)
where vars_kept = Set.fromList vars
substitution_lookup = Map.lookup
substitution_finite_singleton :: Monovar -> Monotype -> Substitution_Finite
substitution_finite_singleton = Map.singleton
substitution_finite :: [(Monovar, Monotype)] -> Substitution_Finite
substitution_finite = Map.fromList
-- | Safely unify two 'Substitution_Finite's.
--
-- NOTE: the union is left-biased:
-- it prefers the first 'Substitution_Finite'
-- when duplicate keys are encountered.
substitution_finite_union :: Substitution_Finite -> Substitution_Finite -> Substitution_Finite
substitution_finite_union x y = x `Map.union` ((x `substitute`) <$> y)
-- | Unsafely unify two 'Substitution_Finite's.
substitution_finite_union_unsafe :: Substitution_Finite -> Substitution_Finite -> Substitution_Finite
substitution_finite_union_unsafe = Map.union
-- ** Type 'Substitution_Fixpoint'
-- | A fixpoint is computed when looking up
-- the target of a 'Monovar' in this 'Substitution'.
-- 'mappend'ing two 'Substitution's is cheap,
-- whereas a 'substitution_lookup' is more expensive
-- than with 'Substitution_Finite'.
newtype Substitution_Fixpoint
= Substitution_Fixpoint (Map Monovar Monotype)
deriving (Show)
instance Empty Substitution_Fixpoint where
empty = Substitution_Fixpoint Map.empty
instance Buildable Substitution_Fixpoint where
build (Substitution_Fixpoint sub) = build sub
-- | WARNING: only disjoint 'Substitution_Fixpoint's can be 'mappend'ed.
instance Monoid Substitution_Fixpoint where
mempty = empty
mappend = substitution_fixpoint_union
instance Substitution Substitution_Fixpoint where
substitution_codomain (Substitution_Fixpoint s) = Map.elems s
substitution_domain (Substitution_Fixpoint s) = Map.keys s
substitution_domain_remove vars (Substitution_Fixpoint s) =
Substitution_Fixpoint $
Map.filterWithKey (\v _ -> v `List.notElem` vars) s
substitution_domain_restrict vars (Substitution_Fixpoint s) =
Substitution_Fixpoint $
Map.filterWithKey (\v _ -> Set.member v vars_kept) s
where vars_kept = Set.fromList vars
-- Map.filterWithKey (\v _ -> v `List.notElem` vars_removed) s
-- where vars_removed = Map.keys s List.\\ vars
substitution_lookup v sub@(Substitution_Fixpoint s) =
case Map.lookup v s of
Nothing -> Nothing
Just ty | ty == Monotype_Var v -> Nothing
| otherwise -> Just $ sub `substitute` ty
substitution_fixpoint :: [(Monovar, Monotype)] -> Substitution_Fixpoint
substitution_fixpoint = Substitution_Fixpoint . Map.fromList
-- | WARNING: only valid with disjoint 'Substitution_Fixpoint's
substitution_fixpoint_union
:: Substitution_Fixpoint
-> Substitution_Fixpoint
-> Substitution_Fixpoint
substitution_fixpoint_union
(Substitution_Fixpoint x)
(Substitution_Fixpoint y) =
Substitution_Fixpoint $
Map.unionWith err x y
where err = error "substitution_fixpoint_union: the two substitutions are not disjoint"
-- ** Type 'Substitution_Instance'
data Substitution_Instance
= forall s. Substitution s
=> Substitution_Instance
{ substitution_instance_substitution :: s
, substitution_instance_codomain :: s -> [Monotype]
, substitution_instance_domain :: s -> [Monovar]
, substitution_instance_remove :: [Monovar] -> s -> s
, substitution_instance_restrict :: [Monovar] -> s -> s
, substitution_instance_lookup :: Monovar -> s -> Maybe Monotype
}
substitution_instance :: Substitution s => s -> Substitution_Instance
substitution_instance s =
Substitution_Instance s
substitution_codomain
substitution_domain
substitution_domain_remove
substitution_domain_restrict
substitution_lookup
instance Substitution Substitution_Instance where
substitution_codomain (Substitution_Instance s f _ _ _ _) = f s
substitution_domain (Substitution_Instance s _ f _ _ _) = f s
substitution_domain_remove vs (Substitution_Instance s _ _ f _ _) = substitution_instance (f vs s)
substitution_domain_restrict vs (Substitution_Instance s _ _ _ f _) = substitution_instance (f vs s)
substitution_lookup v (Substitution_Instance s _ _ _ _ f) = f v s
-- * Class 'Substitutable'
class Substitutable a where
-- | /substitutable type variables/ (aka. /free type variables/).
subvars :: a -> [Monovar]
-- | /idempotent substitution application/.
substitute :: Substitution s => s -> a -> a
instance Substitutable Monotype where
subvars ty =
case ty of
Monotype_Var v -> [v]
Monotype_Const _ -> []
Monotype_App t1 t2 -> subvars t1 `List.union` subvars t2
sub `substitute` ty =
case ty of
Monotype_Var v -> fromMaybe (Monotype_Var v) $ substitution_lookup v sub
Monotype_Const _ -> ty
Monotype_App t1 t2 -> Monotype_App (sub `substitute` t1) (sub `substitute` t2)
instance Substitutable a => Substitutable [a] where
subvars = List.foldr (List.union . subvars) []
substitute sub = ((sub `substitute`) <$>)
instance Substitutable a => Substitutable (Seq a) where
subvars = List.foldr (List.union . subvars) mempty
substitute sub = ((sub `substitute`) <$>)
instance Substitutable a => Substitutable (Maybe a) where
subvars = maybe [] subvars
sub `substitute` m = (sub `substitute`) <$> m
instance (Substitutable a, Substitutable b) => Substitutable (Either a b) where
subvars = either subvars subvars
sub `substitute` e = either (Left . (sub `substitute`)) (Right . (sub `substitute`)) e
-- | Return the next 'Monovar'
-- that is not in the 'subvars'
-- of the given 'Substitutable' value.
--
-- NOTE: return @0@ when 'subvars' is empty
subvar_next :: Substitutable a => a -> Monovar
subvar_next a =
case subvars a of
[] -> 0
vs -> 1 + List.maximum vs