agda2hs-1.2: src/Agda2Hs/Compile/Type.hs
{-# LANGUAGE TypeApplications #-}
module Agda2Hs.Compile.Type where
import Control.Arrow ( (>>>) )
import Control.Monad ( forM, when )
import Control.Monad.Trans ( lift )
import Control.Monad.Reader ( asks )
import Data.List ( find )
import Data.Maybe ( mapMaybe, isJust )
import qualified Language.Haskell.Exts.Syntax as Hs
import qualified Language.Haskell.Exts.Extension as Hs
import qualified Language.Haskell.Exts.Pretty as Hs
import Agda.Compiler.Backend hiding ( Args )
import Agda.Syntax.Common
import Agda.Syntax.Internal
import Agda.Syntax.Common.Pretty ( prettyShow )
import Agda.TypeChecking.Pretty
import Agda.TypeChecking.Reduce ( reduce )
import Agda.TypeChecking.Substitute
import Agda.TypeChecking.Telescope
import Agda.Utils.Impossible ( __IMPOSSIBLE__ )
import Agda.Utils.List ( downFrom )
import Agda.Utils.Maybe ( ifJustM, fromMaybe )
import Agda.Utils.Monad ( ifM, unlessM )
import Agda.Utils.Size ( Sized(size) )
import Agda.Utils.Functor ( ($>) )
import Agda2Hs.Compile.Name ( compileQName )
import Agda2Hs.Compile.Term ( compileVar )
import Agda2Hs.Compile.Types
import Agda2Hs.Compile.Utils
import Agda2Hs.AgdaUtils
import Agda2Hs.HsUtils
isSpecialType :: QName -> Maybe (QName -> Elims -> C (Hs.Type ()))
isSpecialType = prettyShow >>> \case
"Haskell.Prim.Tuple._×_" -> Just tupleType
"Haskell.Prim.Tuple._×_×_" -> Just tupleType
_ -> Nothing
tupleType :: QName -> Elims -> C (Hs.Type ())
tupleType q es = do
let Just as = allApplyElims es
ts <- mapM (compileType . unArg) as
return $ Hs.TyTuple () Hs.Boxed ts
-- | Add a class constraint to a Haskell type.
constrainType
:: Hs.Asst () -- ^ The class assertion.
-> Hs.Type () -- ^ The type to constrain.
-> Hs.Type ()
constrainType c = \case
Hs.TyForall _ as (Just (Hs.CxTuple _ cs)) t -> Hs.TyForall () as (Just (Hs.CxTuple () (c:cs))) t
Hs.TyForall _ as (Just (Hs.CxSingle _ c')) t -> Hs.TyForall () as (Just (Hs.CxTuple () [c,c'])) t
Hs.TyForall _ as _ t -> Hs.TyForall () as (Just (Hs.CxSingle () c)) t
t -> Hs.TyForall () Nothing (Just (Hs.CxSingle () c)) t
-- | Add explicit quantification over a variable to a Haskell type.
qualifyType
:: String -- ^ Name of the variable.
-> Hs.Type () -- ^ Type to quantify.
-> Hs.Type ()
qualifyType s = \case
Hs.TyForall _ (Just as) cs t -> Hs.TyForall () (Just (a:as)) cs t
Hs.TyForall _ Nothing cs t -> Hs.TyForall () (Just [a]) cs t
t -> Hs.TyForall () (Just [a]) Nothing t
where
a = Hs.UnkindedVar () $ Hs.Ident () s
-- | Compile a top-level type, such that:
--
-- * erased parameters of the current module are dropped.
-- * usable hidden type parameters of the current module are explicitely quantified.
-- * usable instance parameters are added as type constraints.
-- * usable explicit parameters are forbidden (for now).
--
-- The continuation is called in an extended context with these type
-- arguments bound.
compileTopLevelType
:: Bool
-- ^ Whether the generated Haskell type will end up in
-- the final output. If so, this functions asks for
-- language extension ScopedTypeVariables to be enabled.
-> Type
-> (Hs.Type () -> C a) -- ^ Continuation with the compiled type.
-> C a
compileTopLevelType keepType t cont = do
reportSDoc "agda2hs.compile.type" 12 $ text "Compiling top-level type" <+> prettyTCM t
-- NOTE(flupe): even though we only quantify variable for definitions inside anonymous modules,
-- they will still get quantified over the toplevel module parameters.
weAreOnTop <- isJust <$> liftTCM (currentModule >>= isTopLevelModule)
modTel <- moduleParametersToDrop =<< currentModule
reportSDoc "agda2hs.compile.type" 19 $ text "Module parameters to process: " <+> prettyTCM modTel
go weAreOnTop modTel cont
where
go :: Bool -> Telescope -> (Hs.Type () -> C a) -> C a
go _ EmptyTel cont = do
ctxArgs <- getContextArgs
ty <- compileType . unEl =<< t `piApplyM` ctxArgs
cont ty
go onTop (ExtendTel a atel) cont
| not (usableModality a) =
underAbstraction a atel $ \tel -> go onTop tel cont
| isInstance a = do
c <- Hs.TypeA () <$> compileType (unEl $ unDom a)
underAbstraction a atel $ \tel ->
go onTop tel (cont . constrainType c)
| otherwise = do
compileType (unEl $ unDom a)
when (keepType && not onTop) $ tellExtension Hs.ScopedTypeVariables
let qualifier = if onTop then id else qualifyType (absName atel)
underAbstraction a atel $ \tel ->
go onTop tel (cont . qualifier)
compileType' :: Term -> C (Strictness, Hs.Type ())
compileType' t = do
s <- case t of
Def f es -> fromMaybe Lazy <$> isUnboxRecord f
_ -> return Lazy
(s,) <$> compileType t
-- | Compile an Agda term into a Haskell type.
compileType :: Term -> C (Hs.Type ())
compileType t = do
reportSDoc "agda2hs.compile.type" 12 $ text "Compiling type" <+> prettyTCM t
reportSDoc "agda2hs.compile.type" 22 $ text "Compiling type" <+> pretty t
case t of
Pi a b -> compileDom (absName b) a >>= \case
DomType _ hsA -> do
hsB <- underAbstraction a b $ compileType . unEl
return $ Hs.TyFun () hsA hsB
DomConstraint hsA -> do
hsB <- underAbstraction a b (compileType . unEl)
return $ constrainType hsA hsB
DomDropped -> underAbstr a b (compileType . unEl)
Def f es -> maybeUnfoldCopy f es compileType $ \f es -> do
def <- getConstInfo f
if | not (usableModality def) ->
genericDocError
=<< text "Cannot use erased definition" <+> prettyTCM f
<+> text "in Haskell type"
| Just semantics <- isSpecialType f -> setCurrentRange f $ semantics f es
| Just args <- allApplyElims es ->
ifJustM (isUnboxRecord f) (\_ -> compileUnboxType f args) $
ifM (isTransparentFunction f) (compileTransparentType args) $ do
vs <- compileTypeArgs args
f <- compileQName f
return $ tApp (Hs.TyCon () f) vs
| otherwise -> fail
Var x es | Just args <- allApplyElims es -> do
vs <- compileTypeArgs args
x <- hsName <$> compileVar x
return $ tApp (Hs.TyVar () x) vs
Sort s -> return (Hs.TyStar ())
Lam argInfo restAbs
| not (keepArg argInfo) -> underAbstraction_ restAbs compileType
_ -> fail
where fail = genericDocError =<< text "Bad Haskell type:" <?> prettyTCM t
compileTypeArgs :: Args -> C [Hs.Type ()]
compileTypeArgs args = mapM (compileType . unArg) $ filter keepArg args
compileUnboxType :: QName -> Args -> C (Hs.Type ())
compileUnboxType r pars = do
def <- theDef <$> getConstInfo r
let tel = telToList $ recTel def `apply` pars
case find keepArg tel of
Nothing -> __IMPOSSIBLE__
Just t -> compileType $ unEl $ snd (unDom t)
compileTransparentType :: Args -> C (Hs.Type ())
compileTransparentType args = compileTypeArgs args >>= \case
[] -> __IMPOSSIBLE__
(v:vs) -> return $ v `tApp` vs
compileDom :: ArgName -> Dom Type -> C CompiledDom
compileDom x a
| usableModality a = case getHiding a of
Instance{} -> DomConstraint . Hs.TypeA () <$> compileType (unEl $ unDom a)
NotHidden -> uncurry DomType <$> compileType' (unEl $ unDom a)
Hidden ->
ifM (canErase $ unDom a)
(return DomDropped)
(genericDocError =<< do text "Implicit type argument not supported: " <+> prettyTCM x)
| otherwise = return DomDropped
compileTeleBinds :: Telescope -> C [Hs.TyVarBind ()]
compileTeleBinds tel =
forM
(mapMaybe
(fmap unArgDom . checkArgDom)
(teleArgNames tel `zip` flattenTel @Type tel))
(uncurry compileKeptTeleBind)
where
checkArgDom (argName, argDom) | keepArg argName = Just (argName, argDom)
checkArgDom _ | otherwise = Nothing
unArgDom (argName, argDom) = (hsName . unArg $ argName, unDom argDom)
compileKeptTeleBind :: Hs.Name () -> Type -> C (Hs.TyVarBind ())
compileKeptTeleBind x t = do
checkValidTyVarName x
case compileKind t of
Just k -> pure $ Hs.UnkindedVar () x -- In the future we may want to show kind annotations
_ -> genericDocError =<<
text "Kind of bound argument not supported:"
<+> parens (text (Hs.prettyPrint x) <> text " : " <> prettyTCM t)
compileKind :: Type -> Maybe (Hs.Kind ())
compileKind t = case unEl t of
Sort (Type _) -> pure (Hs.TyStar ())
Pi a b
| keepArg a -> Hs.TyFun () <$> compileKind (unDom a) <*> compileKind (unAbs b)
| otherwise -> compileKind (unAbs b)
_ -> Nothing -- ^ if the argument is erased, we only compile the rest