Skip to content

Commit 5aebcd2

Browse files
committed
refactor: Use an enum type for primitive definitions
This makes our primitives system more elegant and robust. The core problem with the old approach was that because primitive definitions and their semantics were only tied together by strings, utilising them could often result in partial functions being necessary in awkward places. This would have become clear in upcoming work to build our prelude library. A side effect needed to make this work is that the types of primitive definitions no longer contain metadata. As can be seen in this commit, this has very few knock-on effects: seeing as a user can't manipulate these types, we never did anythin interesting with the metadata anyway. Indeed, some code becomes cleaner as a result. We also remove some tests of behaviour which is nowz correct by construction.
1 parent 37af3fa commit 5aebcd2

File tree

13 files changed

+254
-345
lines changed

13 files changed

+254
-345
lines changed

primer/gen/Primer/Gen/App.hs

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ module Primer.Gen.App (
1010
) where
1111

1212
import Primer.App (Prog (Prog, progImports, progLog, progModules, progSelection, progSmartHoles), defaultLog)
13-
import Primer.Core (ASTDef (ASTDef), Def (DefAST), GlobalName (baseName), Kind (KType), ModuleName (ModuleName), defType, qualifyName)
13+
import Primer.Core (ASTDef (ASTDef), Def (DefAST), GlobalName (baseName), Kind (KType), ModuleName (ModuleName), qualifyName)
1414
import Primer.Core.Utils (forgetTypeMetadata, generateIDs, generateTypeIDs)
1515
import Primer.Module (Module (Module, moduleDefs, moduleName, moduleTypes), moduleDefsQualified, moduleTypesQualified)
1616
import Primer.Name (Name, unsafeMkName)
17+
import Primer.Primitives (defType)
1718
import Primer.Typecheck (Cxt, SmartHoles, extendGlobalCxt, extendTypeDefCxt)
1819

1920
import Primer.Gen.Core.Typed (WT, freshNameForCxt, genChk, genTypeDefGroup, genWTType)

primer/src/Primer/API.hs

+14-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE BlockArguments #-}
12
{-# LANGUAGE GADTs #-}
23

34
-- | The Primer API.
@@ -63,7 +64,7 @@ import Control.Monad.Zip (MonadZip)
6364
import Data.Map qualified as Map
6465
import Data.Text qualified as T
6566
import ListT qualified (toList)
66-
import Optics (ifoldr, over, view, (^.))
67+
import Optics (ifoldr, over, traverseOf, view, (^.))
6768
import Primer.App (
6869
App,
6970
EditAppM,
@@ -107,12 +108,12 @@ import Primer.Core (
107108
Type,
108109
Type' (..),
109110
defAST,
110-
defType,
111111
moduleNamePretty,
112112
unLocalName,
113113
_typeMeta,
114114
_typeMetaLens,
115115
)
116+
import Primer.Core qualified as Core
116117
import Primer.Database (
117118
OffsetLimit,
118119
Page,
@@ -148,6 +149,7 @@ import Primer.JSON (
148149
)
149150
import Primer.Module (moduleDefsQualified, moduleName, moduleTypesQualified)
150151
import Primer.Name (Name, unName)
152+
import Primer.Primitives (primDefType)
151153
import StmContainers.Map qualified as StmMap
152154

153155
-- | The API environment.
@@ -485,8 +487,17 @@ viewProg p =
485487
( \(name, d) ->
486488
Def
487489
{ name
488-
, type_ = viewTreeType $ defType d
489490
, term = viewTreeExpr . astDefExpr <$> defAST d
491+
, type_ =
492+
case d of
493+
Core.DefAST d' -> viewTreeType $ astDefType d'
494+
Core.DefPrim d' -> viewTreeType' $ labelNodes $ primDefType d'
495+
where
496+
labelNodes =
497+
flip evalState (0 :: Int) . traverseOf _typeMeta \() -> do
498+
n <- get
499+
put $ n + 1
500+
pure $ "primtype_" <> show d' <> "_" <> show n
490501
}
491502
)
492503
<$> Map.assocs (moduleDefsQualified m)

primer/src/Primer/Core.hs

+23-33
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ module Primer.Core (
1212
CaseBranch' (..),
1313
Def (..),
1414
DefMap,
15-
_defType,
16-
defType,
1715
ASTDef (..),
1816
defAST,
1917
PrimDef (..),
@@ -55,8 +53,6 @@ module Primer.Core (
5553
PrimTypeDef (..),
5654
PrimCon (..),
5755
primConName,
58-
PrimFun (..),
59-
primFunType,
6056
PrimFunError (..),
6157
ValCon (..),
6258
valConType,
@@ -78,7 +74,6 @@ module Primer.Core (
7874

7975
import Foreword
8076

81-
import Control.Monad.Fresh (MonadFresh (fresh))
8277
import Data.Aeson (Value)
8378
import Data.Data (Data)
8479
import Data.Generics.Product
@@ -93,7 +88,6 @@ import Optics (
9388
afailing,
9489
atraversalVL,
9590
lens,
96-
lensVL,
9791
set,
9892
view,
9993
(%),
@@ -495,10 +489,28 @@ type DefMap = Map GVarName Def
495489
-- Names and definitions of primitives are hard-coded in Primer.Primitives.
496490
-- A @PrimDef@ simply exposes one of those, and thus the type must match
497491
-- the one stored in the corresponding 'PrimFun'.
498-
newtype PrimDef = PrimDef
499-
{ primDefType :: Type
500-
}
501-
deriving (Eq, Show, Data, Generic)
492+
data PrimDef
493+
= ToUpper
494+
| IsSpace
495+
| HexToNat
496+
| NatToHex
497+
| EqChar
498+
| IntAdd
499+
| IntMinus
500+
| IntMul
501+
| IntQuotient
502+
| IntRemainder
503+
| IntQuot
504+
| IntRem
505+
| IntLT
506+
| IntLTE
507+
| IntGT
508+
| IntGTE
509+
| IntEq
510+
| IntNeq
511+
| IntToNat
512+
| IntFromNat
513+
deriving (Eq, Show, Enum, Bounded, Data, Generic)
502514
deriving (FromJSON, ToJSON) via PrimerJSON PrimDef
503515

504516
-- | A top-level definition, built from an 'Expr'
@@ -509,12 +521,6 @@ data ASTDef = ASTDef
509521
deriving (Eq, Show, Data, Generic)
510522
deriving (FromJSON, ToJSON) via PrimerJSON ASTDef
511523

512-
_defType :: Lens' Def Type
513-
_defType = lensVL $ \f -> \case
514-
DefPrim (PrimDef t) -> DefPrim . PrimDef <$> f t
515-
DefAST (ASTDef e t) -> DefAST . ASTDef e <$> f t
516-
defType :: Def -> Type
517-
defType = view _defType
518524
defAST :: Def -> Maybe ASTDef
519525
defAST = \case
520526
DefPrim _ -> Nothing
@@ -537,26 +543,10 @@ primConName = \case
537543
PrimChar _ -> qualifyName (mkSimpleModuleName "Primitives") "Char"
538544
PrimInt _ -> qualifyName (mkSimpleModuleName "Primitives") "Int"
539545

540-
data PrimFun = PrimFun
541-
{ primFunTypes :: forall m. MonadFresh ID m => m ([Type], Type)
542-
-- ^ the function's arguments and return type
543-
, primFunDef :: [Expr' () ()] -> Either PrimFunError (forall m. MonadFresh ID m => m Expr)
544-
}
545-
546-
primFunType :: forall m. MonadFresh ID m => PrimFun -> m Type
547-
primFunType pf = do
548-
(args, res) <- primFunTypes pf
549-
foldrM f res args
550-
where
551-
f x y = do
552-
id <- fresh
553-
pure $ TFun (Meta id Nothing Nothing) x y
554-
555546
data PrimFunError
556547
= -- | We have attempted to apply a primitive function to invalid args.
557548
PrimFunError
558-
GVarName
559-
-- ^ Function name
549+
PrimDef
560550
[Expr' () ()]
561551
-- ^ Arguments
562552
deriving (Eq, Show, Data, Generic)

primer/src/Primer/Core/Utils.hs

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ import Primer.Core (
6060
Kind (KHole),
6161
LVarName,
6262
LocalName (LocalName, unLocalName),
63-
PrimDef (..),
6463
TmVarRef (GlobalVarRef, LocalVarRef),
6564
TyVarName,
6665
Type,
@@ -271,5 +270,5 @@ nextID (DefAST (ASTDef e t)) =
271270
let eid = foldlOf' exprIDs max minBound e
272271
tid = foldlOf' typeIDs max minBound t
273272
in succ $ max eid tid
274-
nextID (DefPrim (PrimDef t)) = succ $ foldlOf' typeIDs max minBound t
273+
nextID (DefPrim _) = 0
275274
{-# INLINE nextID #-}

primer/src/Primer/Eval.hs

+3-6
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ import Primer.Core (
6363
LocalName (LocalName, unLocalName),
6464
LocalNameKind (..),
6565
Meta,
66-
PrimDef (..),
67-
PrimFun (..),
6866
PrimFunError (..),
6967
TmVarRef (..),
7068
TyVarName,
@@ -95,7 +93,7 @@ import Primer.Core.Utils (
9593
import Primer.JSON
9694
import Primer.Name (Name, unName, unsafeMkName)
9795
import Primer.Name.Fresh (isFresh, isFreshTy)
98-
import Primer.Primitives (allPrimDefs)
96+
import Primer.Primitives (PrimDef, primFunDef)
9997
import Primer.Zipper (
10098
ExprZ,
10199
FoldAbove,
@@ -989,9 +987,8 @@ tryPrimFun :: Map GVarName PrimDef -> Expr -> Maybe (GVarName, [Expr], forall m.
989987
tryPrimFun primDefs expr
990988
| -- Since no primitive functions are polymorphic, there is no need to unfoldAPP
991989
(Var _ (GlobalVarRef name), args) <- bimap stripAnns (map stripAnns) $ unfoldApp expr
992-
, Map.member name primDefs
993-
, Just PrimFun{primFunDef} <- Map.lookup name allPrimDefs
994-
, Right e <- primFunDef $ forgetMetadata <$> args =
990+
, Just x <- Map.lookup name primDefs
991+
, Right e <- primFunDef x $ forgetMetadata <$> args =
995992
Just (name, args, e)
996993
| otherwise = Nothing
997994
where

0 commit comments

Comments
 (0)