module Language.PureScript.TypeChecker.Skolems
( newSkolemConstant
, introduceSkolemScope
, newSkolemScope
, skolemize
, skolemizeTypesInValue
, skolemEscapeCheck
) where
import Prelude.Compat
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.State.Class (MonadState(..), gets, modify)
import Data.Foldable (traverse_)
import Data.Functor.Identity (Identity(), runIdentity)
import Data.Monoid
import Data.Set (Set, fromList, notMember)
import Data.Text (Text)
import Language.PureScript.AST
import Language.PureScript.Crash
import Language.PureScript.Errors
import Language.PureScript.Traversals (defS)
import Language.PureScript.TypeChecker.Monad
import Language.PureScript.Types
newSkolemConstant :: MonadState CheckState m => m Int
newSkolemConstant = do
s <- gets checkNextSkolem
modify $ \st -> st { checkNextSkolem = s + 1 }
return s
introduceSkolemScope :: MonadState CheckState m => Type -> m Type
introduceSkolemScope = everywhereOnTypesM go
where
go (ForAll ident ty Nothing) = ForAll ident ty <$> (Just <$> newSkolemScope)
go other = return other
newSkolemScope :: MonadState CheckState m => m SkolemScope
newSkolemScope = do
s <- gets checkNextSkolemScope
modify $ \st -> st { checkNextSkolemScope = s + 1 }
return $ SkolemScope s
skolemize :: Text -> Int -> SkolemScope -> Maybe SourceSpan -> Type -> Type
skolemize ident sko scope ss = replaceTypeVars ident (Skolem ident sko scope ss)
skolemizeTypesInValue :: Text -> Int -> SkolemScope -> Maybe SourceSpan -> Expr -> Expr
skolemizeTypesInValue ident sko scope ss =
runIdentity . onExpr'
where
onExpr' :: Expr -> Identity Expr
(_, onExpr', _, _, _) = everywhereWithContextOnValuesM [] defS onExpr onBinder defS defS
onExpr :: [Text] -> Expr -> Identity ([Text], Expr)
onExpr sco (DeferredDictionary c ts)
| ident `notElem` sco = return (sco, DeferredDictionary c (map (skolemize ident sko scope ss) ts))
onExpr sco (TypedValue check val ty)
| ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedValue check val (skolemize ident sko scope ss ty))
onExpr sco other = return (sco, other)
onBinder :: [Text] -> Binder -> Identity ([Text], Binder)
onBinder sco (TypedBinder ty b)
| ident `notElem` sco = return (sco ++ peelTypeVars ty, TypedBinder (skolemize ident sko scope ss ty) b)
onBinder sco other = return (sco, other)
peelTypeVars :: Type -> [Text]
peelTypeVars (ForAll i ty _) = i : peelTypeVars ty
peelTypeVars _ = []
skolemEscapeCheck :: MonadError MultipleErrors m => Expr -> m ()
skolemEscapeCheck (TypedValue False _ _) = return ()
skolemEscapeCheck expr@TypedValue{} =
traverse_ (throwError . singleError) (toSkolemErrors expr)
where
toSkolemErrors :: Expr -> [ErrorMessage]
(_, toSkolemErrors, _, _, _) = everythingWithContextOnValues (mempty, Nothing) [] (<>) def go def def def
def s _ = (s, [])
go :: (Set SkolemScope, Maybe SourceSpan)
-> Expr
-> ((Set SkolemScope, Maybe SourceSpan), [ErrorMessage])
go (scopes, _) (PositionedValue ss _ _) = ((scopes, Just ss), [])
go (scopes, ssUsed) val@(TypedValue _ _ ty) =
( (allScopes, ssUsed)
, [ ErrorMessage (maybe id ((:) . positionedError) ssUsed [ ErrorInExpression val ]) $
EscapedSkolem name ssBound ty
| (name, scope, ssBound) <- collectSkolems ty
, notMember scope allScopes
]
)
where
newScopes :: [SkolemScope]
newScopes = collectScopes ty
allScopes :: Set SkolemScope
allScopes = fromList newScopes <> scopes
collectScopes :: Type -> [SkolemScope]
collectScopes (ForAll _ t (Just sco)) = sco : collectScopes t
collectScopes ForAll{} = internalError "skolemEscapeCheck: No skolem scope"
collectScopes _ = []
collectSkolems :: Type -> [(Text, SkolemScope, Maybe SourceSpan)]
collectSkolems = everythingOnTypes (++) collect where
collect (Skolem name _ scope srcSpan) = [(name, scope, srcSpan)]
collect _ = []
go scos _ = (scos, [])
skolemEscapeCheck _ = internalError "skolemEscapeCheck: untyped value"