    module Hint.Typecheck (
          typeOf, typeChecks, kindOf, normalizeType, onCompilationError, typeChecksWithDetails
    ) where

    import Control.Monad.Catch

    import Hint.Base
    import Hint.Parsers
    import Hint.Conversions

    import qualified Hint.GHC as GHC

    -- | Returns a string representation of the type of the expression.
    typeOf :: MonadInterpreter m => String -> m String
    typeOf :: forall (m :: * -> *). MonadInterpreter m => String -> m String
    typeOf String
    expr =
        do -- First, make sure the expression has no syntax errors,
           -- for this is the only way we have to "intercept" this
           -- kind of errors
           (String -> m ParseResult) -> String -> m ()
    forall (m :: * -> *).
    MonadInterpreter m =>
    (String -> m ParseResult) -> String -> m ()
    failOnParseError String -> m ParseResult
    forall (m :: * -> *). MonadInterpreter m => String -> m ParseResult
    parseExpr String
    expr
           --
           type_ <- m (Maybe Type) -> m Type
    forall (m :: * -> *) a. MonadInterpreter m => m (Maybe a) -> m a
    mayFail (RunGhc m (Maybe Type)
    forall a. RunGhc m a
    forall (m :: * -> *) a. MonadInterpreter m => RunGhc m a
    runGhc RunGhc m (Maybe Type) -> RunGhc m (Maybe Type)
    forall a b. (a -> b) -> a -> b
    $ String -> GhcT n (Maybe Type)
    forall (m :: * -> *). GhcMonad m => String -> m (Maybe Type)
    exprType String
    expr)
           typeToString type_

    -- | Tests if the expression type checks.
    --
    -- NB. Be careful if @unsafeSetGhcOption "-fdefer-type-errors"@ is used.
    -- Perhaps unsurprisingly, that can falsely make @typeChecks@ and @typeChecksWithDetails@
    -- return @True@ and @Right _@ respectively.
    typeChecks :: MonadInterpreter m => String -> m Bool
    typeChecks :: forall (m :: * -> *). MonadInterpreter m => String -> m Bool
    typeChecks String
    expr = (Bool
    True Bool -> m String -> m Bool
    forall a b. a -> m b -> m a
    forall (f :: * -> *) a b. Functor f => a -> f b -> f a
    <$ String -> m String
    forall (m :: * -> *). MonadInterpreter m => String -> m String
    typeOf String
    expr)
                                  m Bool -> (InterpreterError -> m Bool) -> m Bool
    forall (m :: * -> *) a.
    MonadInterpreter m =>
    m a -> (InterpreterError -> m a) -> m a
    `catchIE`
                                  ([GhcError] -> m Bool) -> InterpreterError -> m Bool
    forall (m :: * -> *) a.
    MonadInterpreter m =>
    ([GhcError] -> m a) -> InterpreterError -> m a
    onCompilationError (\[GhcError]
    _ -> Bool -> m Bool
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False)

    -- | Similar to @typeChecks@, but gives more information, e.g. the type errors.
    typeChecksWithDetails :: MonadInterpreter m => String -> m (Either [GhcError] String)
    typeChecksWithDetails :: forall (m :: * -> *).
    MonadInterpreter m =>
    String -> m (Either [GhcError] String)
    typeChecksWithDetails String
    expr = (String -> Either [GhcError] String
    forall a b. b -> Either a b
    Right (String -> Either [GhcError] String)
    -> m String -> m (Either [GhcError] String)
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    <$> String -> m String
    forall (m :: * -> *). MonadInterpreter m => String -> m String
    typeOf String
    expr)
                                  m (Either [GhcError] String)
    -> (InterpreterError -> m (Either [GhcError] String))
    -> m (Either [GhcError] String)
    forall (m :: * -> *) a.
    MonadInterpreter m =>
    m a -> (InterpreterError -> m a) -> m a
    `catchIE`
                                  ([GhcError] -> m (Either [GhcError] String))
    -> InterpreterError -> m (Either [GhcError] String)
    forall (m :: * -> *) a.
    MonadInterpreter m =>
    ([GhcError] -> m a) -> InterpreterError -> m a
    onCompilationError (Either [GhcError] String -> m (Either [GhcError] String)
    forall a. a -> m a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either [GhcError] String -> m (Either [GhcError] String))
    -> ([GhcError] -> Either [GhcError] String)
    -> [GhcError]
    -> m (Either [GhcError] String)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [GhcError] -> Either [GhcError] String
    forall a b. a -> Either a b
    Left)

    -- | Returns a string representation of the kind of the type expression.
    kindOf :: MonadInterpreter m => String -> m String
    kindOf :: forall (m :: * -> *). MonadInterpreter m => String -> m String
    kindOf String
    type_expr =
        do -- First, make sure the expression has no syntax errors,
           -- for this is the only way we have to "intercept" this
           -- kind of errors
           (String -> m ParseResult) -> String -> m ()
    forall (m :: * -> *).
    MonadInterpreter m =>
    (String -> m ParseResult) -> String -> m ()
    failOnParseError String -> m ParseResult
    forall (m :: * -> *). MonadInterpreter m => String -> m ParseResult
    parseType String
    type_expr
           --
           (_, kind) <- m (Maybe (Type, Type)) -> m (Type, Type)
    forall (m :: * -> *) a. MonadInterpreter m => m (Maybe a) -> m a
    mayFail (m (Maybe (Type, Type)) -> m (Type, Type))
    -> m (Maybe (Type, Type)) -> m (Type, Type)
    forall a b. (a -> b) -> a -> b
    $ RunGhc m (Maybe (Type, Type))
    forall a. RunGhc m a
    forall (m :: * -> *) a. MonadInterpreter m => RunGhc m a
    runGhc RunGhc m (Maybe (Type, Type)) -> RunGhc m (Maybe (Type, Type))
    forall a b. (a -> b) -> a -> b
    $ String -> GhcT n (Maybe (Type, Type))
    forall (m :: * -> *).
    GhcMonad m =>
    String -> m (Maybe (Type, Type))
    typeKind String
    type_expr
           --
           kindToString kind

    -- | Returns a string representation of the normalized type expression.
    -- This is what the @:kind!@ GHCi command prints after @=@.
    normalizeType :: MonadInterpreter m => String -> m String
    normalizeType :: forall (m :: * -> *). MonadInterpreter m => String -> m String
    normalizeType String
    type_expr =
        do -- First, make sure the expression has no syntax errors,
           -- for this is the only way we have to "intercept" this
           -- kind of errors
           (String -> m ParseResult) -> String -> m ()
    forall (m :: * -> *).
    MonadInterpreter m =>
    (String -> m ParseResult) -> String -> m ()
    failOnParseError String -> m ParseResult
    forall (m :: * -> *). MonadInterpreter m => String -> m ParseResult
    parseType String
    type_expr
           --
           (ty, _) <- m (Maybe (Type, Type)) -> m (Type, Type)
    forall (m :: * -> *) a. MonadInterpreter m => m (Maybe a) -> m a
    mayFail (m (Maybe (Type, Type)) -> m (Type, Type))
    -> m (Maybe (Type, Type)) -> m (Type, Type)
    forall a b. (a -> b) -> a -> b
    $ RunGhc m (Maybe (Type, Type))
    forall a. RunGhc m a
    forall (m :: * -> *) a. MonadInterpreter m => RunGhc m a
    runGhc RunGhc m (Maybe (Type, Type)) -> RunGhc m (Maybe (Type, Type))
    forall a b. (a -> b) -> a -> b
    $ String -> GhcT n (Maybe (Type, Type))
    forall (m :: * -> *).
    GhcMonad m =>
    String -> m (Maybe (Type, Type))
    typeKind String
    type_expr
           --
           typeToString ty

    -- add a bogus Maybe, in order to use it with mayFail
    exprType :: GHC.GhcMonad m => String -> m (Maybe GHC.Type)
    exprType :: forall (m :: * -> *). GhcMonad m => String -> m (Maybe Type)
    exprType = (Type -> Maybe Type) -> m Type -> m (Maybe Type)
    forall a b. (a -> b) -> m a -> m b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Type -> Maybe Type
    forall a. a -> Maybe a
    Just (m Type -> m (Maybe Type))
    -> (String -> m Type) -> String -> m (Maybe Type)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . TcRnExprMode -> String -> m Type
    forall (m :: * -> *).
    GhcMonad m =>
    TcRnExprMode -> String -> m Type
    GHC.exprType TcRnExprMode
    GHC.TM_Inst

    -- add a bogus Maybe, in order to use it with mayFail
    typeKind :: GHC.GhcMonad m => String -> m (Maybe (GHC.Type, GHC.Kind))
    typeKind :: forall (m :: * -> *).
    GhcMonad m =>
    String -> m (Maybe (Type, Type))
    typeKind = ((Type, Type) -> Maybe (Type, Type))
    -> m (Type, Type) -> m (Maybe (Type, Type))
    forall a b. (a -> b) -> m a -> m b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap (Type, Type) -> Maybe (Type, Type)
    forall a. a -> Maybe a
    Just (m (Type, Type) -> m (Maybe (Type, Type)))
    -> (String -> m (Type, Type)) -> String -> m (Maybe (Type, Type))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Bool -> String -> m (Type, Type)
    forall (m :: * -> *).
    GhcMonad m =>
    Bool -> String -> m (Type, Type)
    GHC.typeKind Bool
    True

    onCompilationError :: MonadInterpreter m
                       => ([GhcError] -> m a)
                       -> (InterpreterError -> m a)
    onCompilationError :: forall (m :: * -> *) a.
    MonadInterpreter m =>
    ([GhcError] -> m a) -> InterpreterError -> m a
    onCompilationError [GhcError] -> m a
    recover InterpreterError
    interp_error
        = case InterpreterError
    interp_error of
              WontCompile [GhcError]
    errs -> [GhcError] -> m a
    recover [GhcError]
    errs
              InterpreterError
    otherErr         -> InterpreterError -> m a
    forall e a. (HasCallStack, Exception e) => e -> m a
    forall (m :: * -> *) e a.
    (MonadThrow m, HasCallStack, Exception e) =>
    e -> m a
    throwM InterpreterError
    otherErr