Skip to content

Commit f862cbc

Browse files
committed
[ #242 ] Implement GADT syntax
1 parent 3bf65eb commit f862cbc

File tree

10 files changed

+79
-22
lines changed

10 files changed

+79
-22
lines changed

src/Agda2Hs/Compile.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,16 @@ compile genv tlm _ def =
126126
(CompileToPragma s , Function{}) -> [] <$ checkCompileToFunctionPragma def s
127127

128128
(ClassPragma ms , Record{} ) -> pure <$> compileRecord (ToClass ms) def
129-
(NewTypePragma ds , Record{} ) -> pure <$> compileRecord (ToRecord True ds) def
130-
(NewTypePragma ds , Datatype{}) -> compileData True ds def
131-
(DefaultPragma ds , Datatype{}) -> compileData False ds def
129+
(NewTypePragma ds , Record{} ) -> pure <$> compileRecord (ToRecord ToNewType ds) def
130+
(NewTypePragma ds , Datatype{}) -> compileData ToNewType ds def
131+
(GadtPragma ds , Datatype{}) -> compileData ToGadt ds def
132+
(DefaultPragma ds , Datatype{}) -> compileData ToData ds def
132133
(DerivePragma s , _ ) | isInstance -> pure <$> compileInstance (ToDerivation s) def
133134
(DefaultPragma _ , Axiom{} ) | isInstance -> pure <$> compileInstance (ToDerivation Nothing) def
134135
(DefaultPragma _ , _ ) | isInstance -> pure <$> compileInstance ToDefinition def
135136
(DefaultPragma _ , Axiom{} ) -> compilePostulate def
136137
(DefaultPragma _ , Function{}) -> compileFun True def
137-
(DefaultPragma ds , Record{} ) -> pure <$> compileRecord (ToRecord False ds) def
138+
(DefaultPragma ds , Record{} ) -> pure <$> compileRecord (ToRecord ToData ds) def
138139

139140
_ -> agda2hsErrorM $ text "Don't know how to compile" <+> prettyTCM (defName def)
140141

src/Agda2Hs/Compile/Data.hs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,13 @@ import Agda.TypeChecking.Telescope
1313

1414
import Agda.Utils.Maybe
1515
import Agda.Utils.Monad
16+
import Agda.Utils.Singleton
1617
import Agda.Utils.Impossible ( __IMPOSSIBLE__ )
1718

1819
import Agda2Hs.AgdaUtils
1920

2021
import Agda2Hs.Compile.Name
21-
import Agda2Hs.Compile.Type ( compileDomType, compileTeleBinds, compileDom, DomOutput(..) )
22+
import Agda2Hs.Compile.Type ( compileType, compileDomType, compileTeleBinds, compileDom, DomOutput(..) )
2223
import Agda2Hs.Compile.Types
2324
import Agda2Hs.Compile.Utils
2425

@@ -34,8 +35,8 @@ checkNewtype name cs = do
3435
(Hs.QualConDecl () _ _ (Hs.ConDecl () cName types):_) -> checkNewtypeCon cName types
3536
_ -> __IMPOSSIBLE__
3637

37-
compileData :: AsNewType -> [Hs.Deriving ()] -> Definition -> C [Hs.Decl ()]
38-
compileData newtyp ds def = do
38+
compileData :: DataTarget -> [Hs.Deriving ()] -> Definition -> C [Hs.Decl ()]
39+
compileData target ds def = do
3940
let d = hsName $ prettyShow $ qnameName $ defName def
4041
checkValidTypeName d
4142
let Datatype{dataPars = n, dataIxs = numIxs, dataCons = cs} = theDef def
@@ -48,12 +49,22 @@ compileData newtyp ds def = do
4849
-- TODO: filter out erased constructors
4950
cs <- mapM (compileConstructor params) cs
5051
let hd = foldl (Hs.DHApp ()) (Hs.DHead () d) binds
52+
let htarget = toDataTarget target
5153

52-
let target = if newtyp then Hs.NewType () else Hs.DataType ()
54+
when (target == ToNewType) (checkNewtype d $ map fst cs)
5355

54-
when newtyp (checkNewtype d cs)
56+
return . singleton $ case target of
57+
ToGadt -> Hs.GDataDecl () htarget Nothing hd Nothing (map (uncurry conToGadtCon) cs) ds
58+
_ -> Hs.DataDecl () htarget Nothing hd (map fst cs) ds
5559

56-
return [Hs.DataDecl () target Nothing hd cs ds]
60+
where
61+
conToGadtCon :: Hs.QualConDecl () -> Hs.Type () -> Hs.GadtDecl ()
62+
conToGadtCon (Hs.QualConDecl _ tys ctx con) rt = case con of
63+
Hs.ConDecl () c ts ->
64+
Hs.GadtDecl () c tys ctx Nothing $
65+
foldr (Hs.TyFun ()) rt ts
66+
Hs.InfixConDecl{} -> __IMPOSSIBLE__
67+
Hs.RecDecl{} -> __IMPOSSIBLE__
5768

5869
allIndicesErased :: Type -> C ()
5970
allIndicesErased t = reduce (unEl t) >>= \case
@@ -64,19 +75,20 @@ allIndicesErased t = reduce (unEl t) >>= \case
6475
DomForall{} -> agda2hsError "Not supported: indexed datatypes"
6576
_ -> return ()
6677

67-
compileConstructor :: [Arg Term] -> QName -> C (Hs.QualConDecl ())
78+
compileConstructor :: [Arg Term] -> QName -> C (Hs.QualConDecl (), Hs.Type ())
6879
compileConstructor params c = do
6980
reportSDoc "agda2hs.data.con" 15 $ text "compileConstructor" <+> prettyTCM c
7081
reportSDoc "agda2hs.data.con" 20 $ text " params = " <+> prettyTCM params
7182
ty <- defType <$> getConstInfo c
7283
reportSDoc "agda2hs.data.con" 30 $ text " ty (before piApply) = " <+> prettyTCM ty
7384
ty <- ty `piApplyM` params
7485
reportSDoc "agda2hs.data.con" 20 $ text " ty = " <+> prettyTCM ty
75-
TelV tel _ <- telView ty
86+
TelV tel ret <- telView ty
7687
let conName = hsName $ prettyShow $ qnameName c
7788
checkValidConName conName
7889
args <- compileConstructorArgs tel
79-
return $ Hs.QualConDecl () Nothing Nothing $ Hs.ConDecl () conName args
90+
hret <- addContext tel $ compileType $ unEl ret
91+
return (Hs.QualConDecl () Nothing Nothing $ Hs.ConDecl () conName args, hret)
8092

8193
compileConstructorArgs :: Telescope -> C [Hs.Type ()]
8294
compileConstructorArgs EmptyTel = return []
@@ -132,12 +144,13 @@ checkCompileToDataPragma def s = noCheckNames $ do
132144
unless (length rcons == length dcons) $ fail
133145
"they have a different number of constructors"
134146
forM_ (zip dcons rcons) $ \(c1, c2) -> do
135-
Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC1 args1) <-
147+
(Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC1 args1) , rt1) <-
136148
addContext dtel $ compileConstructor (teleArgs dtel) c1
137149
-- rename parameters of r to match those of d
138150
rtel' <- renameParameters dtel rtel
139-
Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC2 args2) <-
151+
(Hs.QualConDecl _ _ _ (Hs.ConDecl _ hsC2 args2) , rt2) <-
140152
addContext rtel' $ compileConstructor (teleArgs rtel') c2
153+
-- TODO: check that rt1 and rt2 are equal?
141154
unless (hsC1 == hsC2) $ fail $
142155
"name of constructor" <+> text (Hs.pp hsC1) <+>
143156
"does not match" <+> text (Hs.pp hsC2)

src/Agda2Hs/Compile/Record.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,14 @@ compileRecord target def = do
114114
assts -> Just (Hs.CxTuple () assts)
115115
defaultDecls <- compileMinRecords def ms
116116
return $ Hs.ClassDecl () context hd [] (Just (classDecls ++ map (Hs.ClsDecl ()) defaultDecls))
117-
ToRecord newtyp ds -> do
117+
ToRecord dtarget ds -> do
118118
checkValidConName cName
119119
when (theEtaEquality recEtaEquality' == YesEta) $ agda2hsErrorM $
120120
"Agda records compiled to Haskell should have eta-equality disabled." <+>
121121
"Add no-eta-equality to the definition of" <+> (text (pp rName) <> ".")
122122
(constraints, fieldDecls) <- compileRecFields fieldDecl recFields fieldTel
123-
when newtyp $ checkNewtypeCon cName fieldDecls
124-
let target = if newtyp then Hs.NewType () else Hs.DataType ()
123+
when (dtarget == ToNewType) $ checkNewtypeCon cName fieldDecls
124+
let target = toDataTarget dtarget
125125
compileDataRecord constraints fieldDecls target hd ds
126126

127127
where

src/Agda2Hs/Compile/Types.hs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -210,12 +210,17 @@ data CompiledDom
210210
| DomDropped
211211
-- ^ To nothing (e.g. erased proofs)
212212

213-
-- | Whether a datatype/record should be compiled as a @newtype@ haskell definition.
214-
type AsNewType = Bool
213+
-- | Whether a datatype/record should be compiled as a @newtype@ haskell definition,
214+
-- a gadt-style datatype, or a regular datatype.
215+
data DataTarget
216+
= ToNewType
217+
| ToGadt
218+
| ToData
219+
deriving (Eq)
215220

216221
-- | Compilation target for an Agda record definition.
217222
data RecordTarget
218-
= ToRecord AsNewType [Hs.Deriving ()]
223+
= ToRecord DataTarget [Hs.Deriving ()]
219224
| ToClass [String]
220225

221226
-- | Compilation target for an Agda instance declaration.

src/Agda2Hs/Compile/Utils.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ infixr 0 /\, \/
106106
f /\ g = \x -> f x && g x
107107
f \/ g = \x -> f x || g x
108108

109+
toDataTarget :: DataTarget -> Hs.DataOrNew ()
110+
toDataTarget ToNewType = Hs.NewType ()
111+
toDataTarget ToData = Hs.DataType ()
112+
toDataTarget ToGadt = Hs.DataType ()
113+
109114
showTCM :: PrettyTCM a => a -> C String
110115
showTCM x = liftTCM $ show <$> prettyTCM x
111116

@@ -200,6 +205,7 @@ hasCompilePragma q = processPragma q <&> \case
200205
TransparentPragma{} -> True
201206
CompileToPragma{} -> True
202207
NewTypePragma{} -> True
208+
GadtPragma{} -> True
203209
DerivePragma{} -> True
204210

205211
-- Exploits the fact that the name of the record type and the name of the record module are the

src/Agda2Hs/Pragma.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ data ParsedPragma
5656
| TransparentPragma
5757
| NewTypePragma [Hs.Deriving ()]
5858
| TuplePragma Hs.Boxed
59+
| GadtPragma [Hs.Deriving ()]
5960
| CompileToPragma String
6061
| DerivePragma (Maybe (Hs.DerivStrategy ()))
6162
deriving (Eq, Show)
@@ -72,6 +73,9 @@ parseStrategy _ = Nothing
7273
newtypePragma :: String
7374
newtypePragma = "newtype"
7475

76+
gadtPragma :: String
77+
gadtPragma = "gadt"
78+
7579
processDeriving :: Range -> String -> ([Hs.Deriving ()] -> ParsedPragma) -> C ParsedPragma
7680
processDeriving r d pragma = do
7781
-- parse a deriving clause for a datatype by tacking it onto a
@@ -95,10 +99,12 @@ processPragma qn = liftTCM (getUniqueCompilerPragma pragmaName qn) >>= \case
9599
| s == "transparent" -> return TransparentPragma
96100
| s == "tuple" -> return $ TuplePragma Hs.Boxed
97101
| s == "unboxed-tuple" -> return $ TuplePragma Hs.Unboxed
102+
| s == "gadt" -> return $ GadtPragma []
98103
| "to" `isPrefixOf` s -> return $ CompileToPragma (drop 3 s)
99104
| s == newtypePragma -> return $ NewTypePragma []
100105
| s == derivePragma -> return $ DerivePragma Nothing
101106
| derivePragma `isPrefixOf` s -> return $ DerivePragma (parseStrategy (drop (length derivePragma + 1) s))
102107
| "deriving" `isPrefixOf` s -> processDeriving r s DefaultPragma
103108
| (newtypePragma ++ " deriving") `isPrefixOf` s -> processDeriving r (drop (length newtypePragma + 1) s) NewTypePragma
109+
| (gadtPragma ++ " deriving") `isPrefixOf` s -> processDeriving r (drop (length gadtPragma + 1) s) GadtPragma
104110
_ -> return $ DefaultPragma []

test/AllTests.agda

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# OPTIONS --prop #-}
1+
{-# OPTIONS --prop --polarity #-}
22
module AllTests where
33

44
import AllCubicalTests
@@ -106,6 +106,7 @@ import Issue306
106106
import RelevantDotPattern1
107107
import RelevantDotPattern2
108108
import RelevantDotPattern3
109+
import GadtSyntax
109110

110111
{-# FOREIGN AGDA2HS
111112
import Issue14
@@ -209,4 +210,5 @@ import Issue306
209210
import RelevantDotPattern1
210211
import RelevantDotPattern2
211212
import RelevantDotPattern3
213+
import GadtSyntax
212214
#-}

test/GadtSyntax.agda

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{-# OPTIONS --polarity #-}
2+
3+
open import Haskell.Prelude
4+
5+
data Bol : Set where Tru Fls : Bol
6+
7+
{-# COMPILE AGDA2HS Bol gadt #-}
8+
9+
data Free (f : @++ Set Set) (a : Set) : Set where
10+
Return : a Free f a
11+
Roll : f (Free f a) Free f a
12+
13+
{-# COMPILE AGDA2HS Free gadt #-}

test/golden/AllTests.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@ import Issue306
101101
import RelevantDotPattern1
102102
import RelevantDotPattern2
103103
import RelevantDotPattern3
104+
import GadtSyntax
104105

test/golden/GadtSyntax.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
module GadtSyntax where
2+
3+
data Bol where
4+
Tru :: Bol
5+
Fls :: Bol
6+
7+
data Free f a where
8+
Return :: a -> Free f a
9+
Roll :: f (Free f a) -> Free f a
10+

0 commit comments

Comments
 (0)