-- Synthing the type of a single expression in the context of an asy
synthExprInIsolation :: (?globals :: Globals)
  => AST () ()
  -> Expr () ()
  -> IO (Either (NonEmpty CheckerError) (Either TypeScheme Kind))
synthExprInIsolation ast@(AST dataDecls defs imports hidden name) expr =
  evalChecker (initState { allHiddenNames = hidden }) $ (do
      _    <- checkNameClashes ast
      _    <- runAll checkTyCon (Primitives.dataTypes ++ dataDecls)
      _    <- runAll checkDataCons (Primitives.dataTypes ++ dataDecls)
      defs <- runAll kindCheckDef defs
      let defCtxt = map (\(Def _ name _ tys) -> (name, tys)) defs
      -- Since we need to return a type scheme, have a look first
      -- for top-level identifiers with their schemes
      case expr of
        -- Lookup in data constructors
        (Val s _ (Constr _ c [])) -> do
          mConstructor <- lookupDataConstructor s c
          case mConstructor of
            Just (tySch, _) -> return $ Left tySch
            Nothing -> do
              st <- get
              -- Or see if this is a kind constructors
              case lookup c (Primitives.typeConstructors <> (typeConstructors st)) of
                Just (k, _, _) -> return $ Right k
                Nothing -> throw UnboundDataConstructor{ errLoc = s, errId = c }

        -- Lookup in definitions
        (Val s _ (Var _ x)) -> do
          case lookup x (defCtxt <> Primitives.builtins) of
            Just tyScheme -> return $ Left tyScheme
            Nothing -> throw UnboundVariableError{ errLoc = s, errId = x }

        -- Otherwise, do synth
        _ -> do
          (ty, _, _, _) <- synthExpr defCtxt [] Positive expr
          return $ Left $ Forall nullSpanNoFile [] [] ty)
synthExprInIsolation :: (?globals :: Globals)
  => AST () ()
  -> Expr () ()
  -> IO (Either (NonEmpty CheckerError) (Either TypeScheme Kind))
synthExprInIsolation ast@(AST dataDecls defs imports hidden name) expr =
  evalChecker (initState { allHiddenNames = hidden }) $ (do
      _    <- checkNameClashes ast
      _    <- runAll checkTyCon (Primitives.dataTypes ++ dataDecls)
      _    <- runAll checkDataCons (Primitives.dataTypes ++ dataDecls)
      defs <- runAll kindCheckDef defs
      let defCtxt = map (\(Def _ name _ tys) -> (name, tys)) defs
      -- Since we need to return a type scheme, have a look first
      -- for top-level identifiers with their schemes
      case expr of
        -- Lookup in data constructors
        (Val s _ (Constr _ c [])) -> do
          mConstructor <- lookupDataConstructor s c
          case mConstructor of
            Just (tySch, _) -> return $ Left tySch
            Nothing -> do
              st <- get
              -- Or see if this is a kind constructors
              case lookup c (Primitives.typeConstructors <> (typeConstructors st)) of
                Just (k, _, _) -> return $ Right k
                Nothing -> throw UnboundDataConstructor{ errLoc = s, errId = c }

        -- Lookup in definitions
        (Val s _ (Var _ x)) -> do
          case lookup x (defCtxt <> Primitives.builtins) of
            Just tyScheme -> return $ Left tyScheme
            Nothing -> throw UnboundVariableError{ errLoc = s, errId = x }

        -- Otherwise, do synth
        _ -> do
          (ty, _, _, _) <- synthExpr defCtxt [] Positive expr
          return $ Left $ Forall nullSpanNoFile [] [] ty)
synthExprInIsolation ast@(AST dataDecls defs imports hidden name) expr =
  evalChecker (initState { allHiddenNames = hidden }) $ (do
      _    <- checkNameClashes ast
      _    <- runAll checkTyCon (Primitives.dataTypes ++ dataDecls)
      _    <- runAll checkDataCons (Primitives.dataTypes ++ dataDecls)
      defs <- runAll kindCheckDef defs
      let defCtxt = map (\(Def _ name _ tys) -> (name, tys)) defs
      -- Since we need to return a type scheme, have a look first
      -- for top-level identifiers with their schemes
      case expr of
        -- Lookup in data constructors
        (Val s _ (Constr _ c [])) -> do
          mConstructor <- lookupDataConstructor s c
          case mConstructor of
            Just (tySch, _) -> return $ Left tySch
            Nothing -> do
              st <- get
              -- Or see if this is a kind constructors
              case lookup c (Primitives.typeConstructors <> (typeConstructors st)) of
                Just (k, _, _) -> return $ Right k
                Nothing -> throw UnboundDataConstructor{ errLoc = s, errId = c }

        -- Lookup in definitions
        (Val s _ (Var _ x)) -> do
          case lookup x (defCtxt <> Primitives.builtins) of
            Just tyScheme -> return $ Left tyScheme
            Nothing -> throw UnboundVariableError{ errLoc = s, errId = x }

        -- Otherwise, do synth
        _ -> do
          (ty, _, _, _) <- synthExpr defCtxt [] Positive expr
          return $ Left $ Forall nullSpanNoFile [] [] ty)