lol-typing-1.20160822: Language/LOL/Typing/Solver/Polytype.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 /parametric polymorphism/.
module Language.LOL.Typing.Solver.Polytype where
import Control.Monad (Monad(..), mapM, forM_, foldM, sequence)
import qualified Control.Monad.Classes as MC
import Data.Bool
import Data.Eq (Eq(..))
import Data.Function (($), flip)
import Data.Functor ((<$>))
import qualified Data.List as List
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Maybe (Maybe(..))
import Data.Monoid (Monoid(..), (<>))
import Data.Text.Buildable (Buildable(..))
import Data.Tuple (snd)
import Prelude (Num(..), error)
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 qualified Language.LOL.Typing.Lib.Control.Monad.Classes.Instance as MC
import Language.LOL.Typing.Lib.Data.Default (Default(..))
import Language.LOL.Typing.Lib.Data.Empty (Empty(..))
import qualified Language.LOL.Typing.Lib.Data.Text.Buildable as Build
-- * Type 'State_Polytype'
data State_Polytype info
= State_Polytype
{ state_polytype_freshvar :: Freshvar
-- ^ An incrementing counter
-- for fresh 'Monovar's, fresh 'Rigvar's, or 'Polyvar's.
, state_polytype_substitution :: Polysub
-- ^ Known 'Polytype's.
, state_polytype_rigids :: [Infoed info ([Rigvar], [Rigtype])]
-- ^ The 'Rigvar's introduced as 'Freshvar's by 'polytype_rigvarify'
-- with the 'Rigtype's from the /typing context/
-- (used by 'polytype_rigids_check').
, state_polytype_quantifiers :: Map Monovar [Quantifier]
-- ^ Map every 'Monovar' unified with an instance of a 'Polytype'
-- to the 'Freshvar's allocated to instantiate
-- the 'quantifiers' of this 'Polytype'.
} deriving (Show)
-- | Make 'State_Polytype' collectable as a 'State' instance
-- out of a 'Monad' stack.
type instance MC.Class State (State_Polytype info) = 'True
instance Buildable info => Buildable (State_Polytype info) where
build State_Polytype
{ state_polytype_freshvar = freshvar
, state_polytype_substitution = polytys
, state_polytype_rigids = rigids
, state_polytype_quantifiers = instances
} =
Build.unlines $
[ "freshvar: " <> build freshvar
, "rigids: " ] <>
((" " <>) <$>
List.foldr (\(Infoed info (rigvas, monotys)) ->
((Build.tuple
[ Build.list rigvas
, Build.list monotys
] <> " {- " <> build info <> " -}") :)
) [] rigids) <>
[ "polytypes: " ] <>
Map.foldrWithKey (\v ty ->
((" p" <> build v <> " == " <>
build (def::Quantification_Build_Options, ty)) :)
) [] polytys <>
[ "instances: " ] <>
Map.foldrWithKey (\v vs ->
((" " <> build (Monotype_Var v) <> " -> " <>
Build.list (Monotype_Var <$> vs)) :)
) [] instances
instance Buildable info => State (State_Polytype info) where
state_name _ = "State_Polytype"
state_show = Build.text
instance Empty (State_Polytype m) where
empty =
State_Polytype
{ state_polytype_freshvar = 0
, state_polytype_substitution = mempty
, state_polytype_rigids = mempty
, state_polytype_quantifiers = mempty
}
-- * Class 'Solver_Polytype'
class
( Solver_Constraint m
, MC.MonadState (State_Polytype (Info m)) m
, Infoable Info_Polytype (Info m)
, Solver_Logable Log_Polytype m
) => Solver_Polytype m where
error_polytype :: Error_Polytype -> Error m
polytype_freshvar :: m Freshvar
polytype_freshvar =
MC.gets $ \(s::State_Polytype (Info m)) ->
state_polytype_freshvar s
polytype_freshvar_set :: Freshvar -> m ()
polytype_freshvar_set state_polytype_freshvar =
MC.modify $ \(s::State_Polytype (Info m)) ->
s{ state_polytype_freshvar }
polytype_substitution :: m Polysub
polytype_substitution =
MC.gets $ \(s::State_Polytype (Info m)) ->
state_polytype_substitution s
polytype_insert :: Polyvar -> Polytype -> m ()
polytype_insert var polyty = do
log $ Log_Polytype_Insert var polyty
MC.modify $ \(s::State_Polytype (Info m)) ->
s{ state_polytype_substitution =
Map.insert var polyty $
state_polytype_substitution s
}
polytype_lookup
:: Polytyref -> m Polytype
polytype_lookup polyref =
case polyref of
Polytyref polyty -> return polyty
Polytyref_Var v ->
Map.findWithDefault
(error "polytype_lookup: Polytyref_Var not found in polytype_substitution")
v <$> polytype_substitution
-- | 'Rigvar' variables
--
-- NOTE: used by 'polytype_rigids_check'.
polytype_rigids :: m [Infoed (Info m) ([Rigvar], [Rigtype])]
polytype_rigids =
MC.gets $ \(s::State_Polytype (Info m)) ->
state_polytype_rigids s
polytype_rigids_set :: [Infoed (Info m) ([Rigvar], [Rigtype])] -> m ()
polytype_rigids_set state_polytype_rigids =
MC.modify $ \(s::State_Polytype (Info m)) ->
s{ state_polytype_rigids }
polytype_rigids_push
:: Infoed (Info m) ([Rigvar], [Rigtype]) -> m ()
polytype_rigids_push rig{-@(Infoed info (vs, ms))-} = do
--constraint_log "polytype_rigids_push"
-- (Infoed info (Build.tuple [Build.list vs, Build.list ms]))
rigs <- polytype_rigids
polytype_rigids_set (rig:rigs)
-- | Return the 'quantified' of the given 'Forall'
-- with the 'quantifiers' 'substitute'd with 'Freshvar's.
-- Also, if the given 'Monotype' is a 'Monotype_Var',
-- and at least one 'Freshvar' has been allocated,
-- record them in the 'State_Polytype'.
polytype_instantiate
:: Substitutable a => Monotype -> Forall a -> m a
polytype_instantiate monoty quant = do
freshvar <- polytype_freshvar
let Unquantification freshvar' a = unquantify_forall freshvar quant
polytype_freshvar_set freshvar'
case monoty of
Monotype_Var monovar ->
case [freshvar..freshvar'-1] of
[] -> return ()
fs ->
MC.modify $ \(s::State_Polytype (Info m)) ->
s{ state_polytype_quantifiers =
Map.insert monovar fs $
state_polytype_quantifiers s
}
_ -> return ()
return a
polytype_quantifiers :: m (Map Monovar [Quantifier])
polytype_quantifiers =
MC.gets $ \(s::State_Polytype (Info m)) ->
state_polytype_quantifiers s
-- | Like 'rigvarify', but 'unquantify'
-- by using fresh 'Monovar's ('Freshvar') instead of fresh 'Const's ('Rigvar's).
--
-- NOTE: useful for the 'quantifiers' of a 'Polytype',
-- maintaining a list of 'Rigvar's in 'state_polytype_rigids'.
polytype_rigvarify
:: Substitutable a
=> Info m -> [Rigtype] -> Forall a -> m a
polytype_rigvarify info rigtys forall_a = do
freshvar <- polytype_freshvar
let Unquantification freshvar' a =
unquantify_forall freshvar forall_a
polytype_freshvar_set freshvar'
polytype_rigids_push $
Infoed info ([freshvar .. freshvar'-1], rigtys)
return a
polytype_rigids_check
:: forall m.
( Solver_Constraint m
, Solver_Monotype m
, Solver_Polytype m
) => m ()
polytype_rigids_check =
polytype_rigids >>=
rigvars_substitution >>=
check_rigvars_are_mapped_to_vars >>=
check_rigvars_are_mapped_to_different_vars >>=
check_monotys_have_no_rigvar >>=
rigvars_substituted >>=
polytype_rigids_set
-- NOTE: restore the 'Rigvar's
-- which are consistent with the current 'monotype_substitution'.
where
-- | Return the 'Monotype's toward which 'monotype_substitution'
-- maps the given 'Rigvar's.
rigvars_substitution
:: [Infoed (Info m) ([Rigvar] , [Rigtype])]
-> m [Infoed (Info m) ([(Rigvar, Monotype)], [Rigtype])]
rigvars_substitution =
mapM $ \(Infoed info (rigvas, rigtys)) -> do
subtys <- monotype_lookup `mapM` rigvas
return $ Infoed info
( List.zip rigvas subtys
, rigtys )
-- | Return the 'Monovar's of the given 'Monotype's which are 'Monotype_Var's.
check_rigvars_are_mapped_to_vars
:: [Infoed (Info m) ([(Rigvar, Monotype)], [Rigtype])]
-> m [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
check_rigvars_are_mapped_to_vars tochecks = do
let (goods, bads) =
List.foldr
(\i@(Infoed info (rigvars_monotys, rigtys)) (gs, bs) ->
let (rigvars_subvars::Maybe [(Rigvar, Monovar)]) =
sequence $ (\(rig, ty) ->
case ty of
Monotype_Var v -> Just (rig, v)
_ -> Nothing
) <$> rigvars_monotys in
case rigvars_subvars of
Just x -> (Infoed info (x, rigtys):gs, bs)
Nothing -> (gs, i:bs)
)
([], [])
tochecks
forM_ bads $ \(Infoed info (rigvars_monotys, _monotys)) ->
constraint_error_insert
(error_polytype $ Error_Polytype_Rigid_type_mismatch rigvars_monotys)
info
return goods
-- | Return the 'Rigvar's which are injectively mapped to the given 'Monovar's.
check_rigvars_are_mapped_to_different_vars
:: [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
-> m [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
check_rigvars_are_mapped_to_different_vars tochecks = do
let (subvars_rigvars::Map Monovar [Rigvar]) =
Map.fromListWith (flip (<>))
[ (subvar, [rigvar])
| Infoed _ (rigvars_subvars, _) <- tochecks
, (rigvar, subvar) <- rigvars_subvars
]
let subvars_colliding_rigvars =
Map.filter
(\l -> case l of { [] -> False; [_] -> False; _ -> True })
subvars_rigvars
let (goods, bads) =
List.partition
(\(Infoed _ (rigvars_subvars, _)) ->
List.null $ (subvars_colliding `List.intersect`) $
snd <$> rigvars_subvars)
tochecks
where subvars_colliding = Map.keys subvars_colliding_rigvars
forM_ bads $ \(Infoed info (rigvars_subvars, _)) ->
constraint_error_insert
(error_polytype $ Error_Polytype_Rigid_injectivity_lost $
Map.fromList
[ (subvar, rigvas)
| (_rigvar, subvar) <- rigvars_subvars
, Just rigvas <- [Map.lookup subvar subvars_colliding_rigvars]
])
info
return goods
check_monotys_have_no_rigvar
:: [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
-> m [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
check_monotys_have_no_rigvar =
foldM (\goods this@(Infoed info (rigvars_subvars, rigtys)) -> do
rigtys' <- monotype_substitute rigtys
case subvars rigtys' `List.intersect` (snd <$> rigvars_subvars) of
[] -> return (this:goods)
rigvars_escaping -> do
constraint_error_insert
(error_polytype $ Error_Polytype_Rigid_escaping rigvars_escaping)
info
return goods
) []
rigvars_substituted
:: [Infoed (Info m) ([(Rigvar, Monovar)], [Rigtype])]
-> m [Infoed (Info m) ([Rigvar], [Rigtype])]
rigvars_substituted checkeds =
return
[ Infoed info (snd <$> rigvars_subvars, rigtys)
| Infoed info ( rigvars_subvars, rigtys) <- checkeds
]
-- * Class 'Info_Polytype'
data Info_Polytype
= Info_Polytype_Instantiated Polytype
| Info_Polytype_Rigidified [Rigtype] Polytype
deriving (Eq, Show)
instance Buildable Info_Polytype where
build x =
case x of
Info_Polytype_Instantiated p ->
"Info_Polytype_Instantiated " <> build p
Info_Polytype_Rigidified rs p ->
"Info_Polytype_Rigidified " <> Build.list rs <> " " <> build p
-- * Type 'Error_Polytype'
data Error_Polytype
= Error_Polytype_Rigid_type_mismatch [(Rigvar, Monotype)]
-- ^ A 'Rigvar' is mapped by the current 'monotype_substitution'
-- to a 'Monotype' which is not a 'Monotype_Var'.
| Error_Polytype_Rigid_injectivity_lost (Map Monovar [Rigvar])
-- ^ 'Map' of 'Monovar's reached by at least two distincts 'Rigvar's.
| Error_Polytype_Rigid_escaping [Rigvar]
-- ^ Some 'Rigvar's escape via a 'Rigtype'.
deriving (Eq, Show)
-- ** Type 'Log_Polytype'
data Log_Polytype
= Log_Polytype_Insert Polyvar Polytype
deriving (Show)
instance Buildable Log_Polytype where
build x =
case x of
Log_Polytype_Insert var polyty ->
"polytype_insert : " <>
build (Polytyref_Var var) <> " == " <> build polyty