Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: display and edit kinds on foralls #1120

Merged
merged 24 commits into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3b135d3
docs: clarify comment on `moveType`
brprice Sep 6, 2023
7d74d5e
docs: explicitly say regenerateExprIDs goes into types
brprice Aug 15, 2023
583031e
refactor: Gen uses TypeG consistently
brprice Aug 16, 2023
c51d1c6
refactor: Primer.Module uses generateTypeDefIDs
brprice Aug 16, 2023
c1ec1e1
test: focusOn tests use exprIDs
brprice Aug 17, 2023
053e07d
refactor: Zipper/Type.hs uses TypeZip' synonym more
brprice Aug 31, 2023
3a76080
refactor: use Loc rather than Either ExprZ TypeZ
brprice Aug 29, 2023
51732fe
refactor: add parameter for kind meta to Expr' and Type'
brprice Aug 15, 2023
51d3630
refactor: remove some typedef traversals
brprice Aug 16, 2023
efb3afc
refactor: generalise TypeZ machinery
brprice Aug 17, 2023
13082f7
refactor: copyPasteBody to avoid nested Either
brprice Sep 5, 2023
7533791
feat: (unused) machinery to express Kind selections
brprice Aug 31, 2023
010b4e6
refactor: abstract out Action.Available:forKind
brprice Sep 5, 2023
67a389c
refactor: applyActionsToTypeSig returns TypeZip+KindTZ
brprice Sep 5, 2023
5e8a53a
refactor: getSelectionTypeOrKind by abstracting viewKindOfKind
brprice Sep 5, 2023
02fbf22
refactor: DSL for Kind' ()
brprice Sep 7, 2023
e9ef32f
feat: add kind ids inside types
brprice Sep 7, 2023
f6f5bf1
refactor: replace temporary khole' with khole (and for kfun, ktype)
brprice Sep 7, 2023
d131378
feat!: change primer-api to emit kinds-in-foralls as trees
brprice Aug 16, 2023
07c9ffc
feat: offer & accept kind actions in foralls
brprice Sep 5, 2023
a521394
refactor: remove findType in favor of findTypeOrKind
brprice Sep 5, 2023
bbca25a
refactor: ParamKindAction uses existing machinery
brprice Sep 7, 2023
6a30798
refactor: ParamKindAction sets the selection
brprice Sep 7, 2023
36ed018
refactor!: ParamKindAction does not take ID, use SetCursor instead
brprice Sep 7, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 51 additions & 44 deletions primer-api/src/Primer/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ import Data.Tuple.Extra (curry3)
import Optics (ifoldr, over, preview, to, traverseOf, view, (%), (^.), _Just)
import Primer.API.NodeFlavor qualified as Flavor
import Primer.API.RecordPair (RecordPair (RecordPair))
import Primer.Action (ActionError, ProgAction, toProgActionInput, toProgActionNoInput)
import Primer.Action (ActionError (ParamNotFound), ProgAction, toProgActionInput, toProgActionNoInput)
import Primer.Action.Available qualified as Available
import Primer.Action.ProgError (ProgError (NodeIDNotFound, ParamNotFound, TypeDefConFieldNotFound))
import Primer.Action.ProgError (ProgError (ActionError, NodeIDNotFound, TypeDefConFieldNotFound))
import Primer.App (
App,
DefSelection (..),
Expand Down Expand Up @@ -178,11 +178,11 @@ import Primer.Core (
ValConName,
getID,
unLocalName,
unsafeMkLocalName,
_bindMeta,
_exprMetaLens,
_kindMeta,
_type,
_typeKindMeta,
_typeMeta,
_typeMetaLens,
)
Expand Down Expand Up @@ -245,7 +245,13 @@ import Primer.Name qualified as Name
import Primer.Primitives (primDefType)
import Primer.TypeDef (ASTTypeDef (..), forgetTypeDefMetadata, typeDefKind, typeDefNameHints, typeDefParameters)
import Primer.TypeDef qualified as TypeDef
import Primer.Zipper (SomeNode (..), findNodeWithParent, findType)
import Primer.Zipper (
SomeNode (..),
findNodeWithParent,
findTypeOrKind,
focusOnKind,
target,
)
import StmContainers.Map qualified as StmMap

-- | The API environment.
Expand Down Expand Up @@ -409,8 +415,8 @@ data APILog
| GetProgram' (ReqResp SessionId Prog)
| GetProgram (ReqResp SessionId App.Prog)
| Edit (ReqResp (SessionId, MutationRequest) (Either ProgError App.Prog))
| VariablesInScope (ReqResp (SessionId, (GVarName, ID)) (Either ProgError (([(TyVarName, Kind' ())], [(LVarName, Type' ())]), [(GVarName, Type' ())])))
| GenerateNames (ReqResp (SessionId, ((GVarName, ID), Either (Maybe (Type' ())) (Maybe (Kind' ())))) (Either ProgError [Name.Name]))
| VariablesInScope (ReqResp (SessionId, (GVarName, ID)) (Either ProgError (([(TyVarName, Kind' ())], [(LVarName, Type' () ())]), [(GVarName, Type' () ())])))
| GenerateNames (ReqResp (SessionId, ((GVarName, ID), Either (Maybe (Type' () ())) (Maybe (Kind' ())))) (Either ProgError [Name.Name]))
| EvalStep (ReqResp (SessionId, EvalReq) (Either ProgError EvalResp))
| EvalFull (ReqResp (SessionId, EvalFullReq) (Either ProgError App.EvalFullResp))
| EvalFull' (ReqResp (SessionId, Maybe TerminationBound, Maybe NormalOrderOptions, GVarName) EvalFullResp)
Expand Down Expand Up @@ -751,7 +757,7 @@ viewProg p =
astTypeDefConstructors t <&> \(TypeDef.ValCon nameCon argsCon) ->
ValCon
{ name = nameCon
, fields = viewTreeType' . over _typeMeta (show . view _id) <$> argsCon
, fields = viewTreeType' . over _typeKindMeta (show . view _id) . over _typeMeta (show . view _id) <$> argsCon
}
}
)
Expand All @@ -767,10 +773,11 @@ viewProg p =
Def.DefPrim d' -> viewTreeType' $ labelNodes $ primDefType d'
where
labelNodes =
flip evalState (0 :: Int) . traverseOf _typeMeta \() -> do
n <- get
put $ n + 1
pure $ "primtype_" <> Name.unName (Core.baseName name) <> "_" <> show n
let f () = do
n <- get
put $ n + 1
pure $ "primtype_" <> Name.unName (Core.baseName name) <> "_" <> show n
in flip evalState (0 :: Int) . (traverseOf _typeKindMeta f <=< traverseOf _typeMeta f)
}
)
<$> Map.assocs (moduleDefsQualified m)
Expand Down Expand Up @@ -955,11 +962,11 @@ viewTreeExpr e0 = case e0 of

-- | Similar to 'viewTreeExpr', but for 'Type's
viewTreeType :: Type -> Tree
viewTreeType = viewTreeType' . over _typeMeta (show . view _id)
viewTreeType = viewTreeType' . over _typeKindMeta (show . view _id) . over _typeMeta (show . view _id)

-- | Like 'viewTreeType', but with the flexibility to accept arbitrary textual node identifiers,
-- rather than using the type's numeric IDs.
viewTreeType' :: Type' Text -> Tree
viewTreeType' :: Type' Text Text -> Tree
viewTreeType' t0 = case t0 of
TEmptyHole _ ->
Tree
Expand Down Expand Up @@ -1006,17 +1013,10 @@ viewTreeType' t0 = case t0 of
TForall _ n k t ->
Tree
{ nodeId
, body = TextBody $ RecordPair Flavor.TForall $ localName $ unsafeMkLocalName $ withKindAnn $ Name.unName $ unLocalName n
, childTrees = [viewTreeType' t]
, body = TextBody $ RecordPair Flavor.TForall $ localName n
, childTrees = [viewTreeKind' k, viewTreeType' t]
, rightChild = Nothing
}
where
-- TODO this is a placeholder
-- for now we expect all kinds in student programs to be `KType`
-- but we show something for other kinds, in order to keep rendering injective
withKindAnn = case k of
KType _ -> identity
_ -> (<> (" :: " <> show k))
TLet _ n t b ->
Tree
{ nodeId
Expand Down Expand Up @@ -1073,14 +1073,14 @@ variablesInScope ::
(MonadIO m, MonadThrow m, MonadAPILog l m) =>
SessionId ->
(GVarName, ID) ->
PrimerM m (Either ProgError (([(TyVarName, Kind' ())], [(LVarName, Type' ())]), [(GVarName, Type' ())]))
PrimerM m (Either ProgError (([(TyVarName, Kind' ())], [(LVarName, Type' () ())]), [(GVarName, Type' () ())]))
variablesInScope = curry $ logAPI (leftResultError VariablesInScope) $ \(sid, (defname, exprid)) ->
liftQueryAppM (handleQuestion (App.VariablesInScope defname exprid)) sid

generateNames ::
(MonadIO m, MonadThrow m, MonadAPILog l m) =>
SessionId ->
((GVarName, ID), Either (Maybe (Type' ())) (Maybe (Kind' ()))) ->
((GVarName, ID), Either (Maybe (Type' () ())) (Maybe (Kind' ()))) ->
PrimerM m (Either ProgError [Name.Name])
generateNames = curry $ logAPI (leftResultError GenerateNames) $ \(sid, ((defname, exprid), tk)) ->
liftQueryAppM (handleQuestion $ GenerateName defname exprid tk) sid
Expand Down Expand Up @@ -1342,49 +1342,48 @@ getSelectionTypeOrKind = curry $ logAPI (noError GetTypeOrKind) $ \(sid, sel0) -
maybe (throw' $ NodeIDNotFound id) (pure . fst) (findNodeWithParent id $ astDefExpr def) <&> \case
ExprNode e -> viewExprType $ e ^. _exprMetaLens
TypeNode t -> viewTypeKind $ t ^. _typeMetaLens
KindNode k -> viewKindOfKind k
CaseBindNode b -> viewExprType $ b ^. _bindMeta
-- sig node selected - get kind from metadata
SigNode ->
maybe (throw' $ NodeIDNotFound id) pure (findType id $ astDefType def) <&> \t ->
viewTypeKind $ t ^. _typeMetaLens
maybe (throw' $ NodeIDNotFound id) pure (findTypeOrKind id $ astDefType def) <&> \case
Left t -> viewTypeKind $ t ^. _typeMetaLens
Right k -> viewKindOfKind k
SelectionTypeDef sel -> do
def <- snd <$> findASTTypeDef allTypeDefs sel.def
case sel.node of
-- type def itself selected - return its kind
Nothing -> pure $ Kind $ viewTreeKind' $ mkIdsK $ typeDefKind $ forgetTypeDefMetadata $ TypeDef.TypeDefAST def
-- param name node selected - return its kind
Just (TypeDefParamNodeSelection (TypeDefParamSelection p Nothing)) ->
maybe (throw' $ ParamNotFound p) (pure . Kind . viewTreeKind . snd) $
find ((== p) . fst) (astTypeDefParameters def)
-- param kind node selected - just return `KType`
-- This is a slight lie, effectively reporting that kinds are types,
-- when this isn't true in Primer (as it is in Haskell with modern GHC's `TypeInType`).
-- But Primer also doesn't (explicitly) have an Agda-style infinite hierarchy of types
-- `True : Bool : Type0 : Type1 : Type2 : ...` (we don't go beyond `Type0` i.e. `KType`),
-- so this is the best that we can easily do.
Just (TypeDefParamNodeSelection (TypeDefParamSelection _ (Just _))) ->
pure $ Kind $ viewTreeKind' $ KType "kind"
Just (TypeDefParamNodeSelection (TypeDefParamSelection p s)) -> do
k <- maybe (throw' $ ActionError $ ParamNotFound p) (pure . snd) $ find ((== p) . fst) (astTypeDefParameters def)
case s of
Nothing ->
-- param name node selected - return its kind
pure . Kind . viewTreeKind $ k
Just i -> do
k' <- maybe (throw' $ NodeIDNotFound i) pure $ focusOnKind i k
pure $ viewKindOfKind $ target k'
-- constructor node selected - return the type to which it belongs
Just (TypeDefConsNodeSelection (TypeDefConsSelection _ Nothing)) ->
pure . Type . viewTreeType' . mkIds $
foldl' (\t -> TApp () t . TVar ()) (TCon () sel.def) (map fst $ astTypeDefParameters def)
-- field node selected - return its kind
Just (TypeDefConsNodeSelection (TypeDefConsSelection c (Just s))) -> do
t0 <- maybe (throw' $ TypeDefConFieldNotFound sel.def c s.index) pure $ getTypeDefConFieldType def c s.index
t <- maybe (throw' $ NodeIDNotFound s.meta) pure $ findType s.meta t0
pure $ viewTypeKind $ t ^. _typeMetaLens
t <- maybe (throw' $ NodeIDNotFound s.meta) pure $ findTypeOrKind s.meta t0
pure $ either (viewTypeKind . view _typeMetaLens) viewKindOfKind t
where
trivialTree = Tree{nodeId = "seltype-0", childTrees = [], rightChild = Nothing, body = NoBody Flavor.EmptyHole}
viewExprType :: ExprMeta -> TypeOrKind
viewExprType = Type . fromMaybe trivialTree . viewExprType'
viewExprType' :: ExprMeta -> Maybe Tree
viewExprType' = preview $ _type % _Just % to (viewTreeType' . mkIds . getAPIType)
isHole :: Type' a -> Bool
isHole :: Type' a b -> Bool
isHole = \case
THole{} -> True
TEmptyHole{} -> True
_ -> False
getAPIType :: TypeCache -> Type' ()
getAPIType :: TypeCache -> Type' () ()
getAPIType = \case
TCSynthed t -> t
TCChkedAt t -> t
Expand All @@ -1396,11 +1395,19 @@ getSelectionTypeOrKind = curry $ logAPI (noError GetTypeOrKind) $ \(sid, sel0) -
-- if neither is a hole (in which case the two are consistent), we choose the synthed type
| otherwise -> tcSynthed
-- We prefix ids to keep them unique from other ids in the emitted program
mkIds :: Type' () -> Type' Text
mkIds = over _typeMeta (("seltype-" <>) . show . getID) . create' . generateTypeIDs
mkIds :: Type' () () -> Type' Text Text
mkIds = over _typeKindMeta (show . view _id) . over _typeMeta (("seltype-" <>) . show . getID) . create' . generateTypeIDs
mkIdsK :: Kind' () -> Kind' Text
mkIdsK = over _kindMeta (("selkind-" <>) . show . getID) . create' . generateKindIDs
viewTypeKind :: TypeMeta -> TypeOrKind
viewTypeKind = Kind . fromMaybe trivialTree . viewTypeKind'
viewTypeKind' :: TypeMeta -> Maybe Tree
viewTypeKind' = preview $ _type % _Just % to (viewTreeKind' . mkIdsK)
-- If a kind node is selected we just return `KType`
-- This is a slight lie, effectively reporting that kinds are types,
-- when this isn't true in Primer (as it is in Haskell with modern GHC's `TypeInType`).
-- But Primer also doesn't (explicitly) have an Agda-style infinite hierarchy of types
-- `True : Bool : Type0 : Type1 : Type2 : ...` (we don't go beyond `Type0` i.e. `KType`),
-- so this is the best that we can easily do.
viewKindOfKind :: Kind' a -> TypeOrKind
viewKindOfKind _ = Kind $ viewTreeKind' $ KType "kind"
4 changes: 2 additions & 2 deletions primer-api/test/Tests/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,11 @@ unit_viewTreeType_injective_var =

unit_viewTreeType_injective_forall_param :: Assertion
unit_viewTreeType_injective_forall_param =
distinctTreeType (tforall "a" (KType ()) tEmptyHole) (tforall "b" (KType ()) tEmptyHole)
distinctTreeType (tforall "a" ktype tEmptyHole) (tforall "b" ktype tEmptyHole)

unit_viewTreeType_injective_forall_kind :: Assertion
unit_viewTreeType_injective_forall_kind =
distinctTreeType (tforall "a" (KType ()) tEmptyHole) (tforall "a" (KHole ()) tEmptyHole)
distinctTreeType (tforall "a" ktype tEmptyHole) (tforall "a" khole tEmptyHole)

distinctTreeExpr :: S Expr -> S Expr -> Assertion
distinctTreeExpr e1 e2 =
Expand Down
Loading