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: push down lets #1111

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
c45f948
test: extra case/let tests
brprice Jul 12, 2023
3ac2cd6
evalFullTestExactSteps
brprice Aug 11, 2023
5b31878
feat: evaluate `let`s by "pushing down", with opt TODO: squash this w…
brprice Oct 31, 2022
372f360
initial crack at push let down
brprice Aug 11, 2023
8bb497c
update tests for not push multi
brprice Aug 11, 2023
a9d0111
update tests for non aggressive elision
brprice Aug 11, 2023
04dc304
update evalfulltests for no pushmulti and no aggressiveelision
brprice Aug 11, 2023
490029f
disable unit 9 for now
brprice Aug 11, 2023
c96b24c
fix some tests for no pushmulti or aggressive elide
brprice Aug 11, 2023
587abf6
update prim partial map steps for new pushlet
brprice Aug 11, 2023
1fe5230
update let tests for no pushmulti/agg elide
brprice Aug 11, 2023
47bad07
add some option plumbing
brprice Aug 11, 2023
bbdbca1
pushMulti opt, library
brprice Aug 11, 2023
bfd8224
option plumb testsuite
brprice Aug 11, 2023
681eb1e
update testsuite for pushMulti
brprice Aug 11, 2023
d45dc43
plumb more testsuite
brprice Aug 11, 2023
34c8dbd
fixup! pushMulti opt, library
brprice Aug 11, 2023
0b35405
final update testsuite pushMulti
brprice Aug 11, 2023
b73d143
better errors for evalFullTest*
brprice Aug 11, 2023
0736afe
todo/review notes - I need to flesh out into a note
brprice Jul 12, 2023
120b54d
aggressiveElision option, library
brprice Aug 11, 2023
6651d90
some plumbing of agg elision in tests
brprice Aug 11, 2023
210b92a
HACKS to get more compact pretty printing
brprice Jul 6, 2023
63d8e2b
REMOVE ME! more brackets in prettyprinting -- sometimes things were a…
brprice Aug 3, 2023
10a8173
tmp: chasing problem with prim partial map
brprice Aug 11, 2023
1b6f562
update testsuite for aggressive elision. some justifications:
brprice Aug 11, 2023
a2837c3
renable unit 8 and 9. TODO: grab msg from 936eb842d84306c106a6ca80991…
brprice Aug 12, 2023
9da8eb3
fixup! pushMulti opt, library
brprice Aug 12, 2023
7297eae
feat: eval combines push and elide
brprice Aug 12, 2023
db35ff0
fix: change eval cxts to match push-down-lets
brprice Jul 12, 2023
eb18e6b
refactor: simplify eval Cxt
brprice Jul 12, 2023
622e34d
fix: Eval.Cxt is a list, can record shadowed lets
brprice Jul 13, 2023
9719b72
refactor: special 'subst' handling is now unneeded
brprice Jul 12, 2023
ac6aea5
fixup! todo/review notes
brprice Jul 24, 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
1 change: 1 addition & 0 deletions primer/primer.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ library
Primer.Eval.Let
Primer.Eval.NormalOrder
Primer.Eval.Prim
Primer.Eval.Push
Primer.Primitives.PrimDef
Primer.Typecheck.Cxt
Primer.Typecheck.Kindcheck
Expand Down
6 changes: 4 additions & 2 deletions primer/src/Primer/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ import Primer.Def (
import Primer.Def.Utils (globalInUse, typeInUse)
import Primer.Eval qualified as Eval
import Primer.Eval.Detail (EvalDetail)
import Primer.Eval.Redex (EvalLog)
import Primer.Eval.Redex (EvalLog, RunRedexOptions (RunRedexOptions,pushAndElide), ViewRedexOptions (ViewRedexOptions, pushMulti))
import Primer.EvalFull (Dir (Syn), EvalFullError (TimedOut), TerminationBound, evalFull)
import Primer.JSON
import Primer.Log (ConvertLogMessage)
Expand Down Expand Up @@ -592,7 +592,9 @@ handleEvalFullRequest ::
handleEvalFullRequest (EvalFullReq{evalFullReqExpr, evalFullCxtDir, evalFullMaxSteps}) = do
app <- ask
let prog = appProg app
result <- runFreshM app $ evalFull (allTypes prog) (allDefs prog) evalFullMaxSteps evalFullCxtDir evalFullReqExpr
let optsV = ViewRedexOptions { pushMulti = True, aggressiveElision = True }
let optsR = RunRedexOptions { pushAndElide = True }
result <- runFreshM app $ evalFull optsV optsR (allTypes prog) (allDefs prog) evalFullMaxSteps evalFullCxtDir evalFullReqExpr
pure $ case result of
Left (TimedOut e) -> EvalFullRespTimedOut e
Right nf -> EvalFullRespNormal nf
Expand Down
42 changes: 26 additions & 16 deletions primer/src/Primer/Eval.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ module Primer.Eval (
-- The public API of this module
step,
redexes,
RunRedexOptions (..),
ViewRedexOptions (..),
EvalLog (..),
EvalError (..),
EvalDetail (..),
Expand All @@ -16,10 +18,11 @@ module Primer.Eval (
GlobalVarInlineDetail (..),
LetRemovalDetail (..),
ApplyPrimFunDetail (..),
PushLetDetail (..),
-- Only exported for testing
Cxt (Cxt),
singletonCxt,
getNonCapturedLocal,
lookupEnclosingLet,
tryReduceExpr,
tryReduceType,
findNodeByID,
Expand Down Expand Up @@ -49,10 +52,11 @@ import Primer.Eval.Detail (
GlobalVarInlineDetail (..),
LetRemovalDetail (..),
LocalVarInlineDetail (..),
PushLetDetail (..),
)
import Primer.Eval.EvalError (EvalError (..))
import Primer.Eval.NormalOrder (
FMExpr (FMExpr, expr, subst, substTy, ty),
FMExpr (FMExpr, expr, ty),
foldMapExpr,
singletonCxt,
)
Expand All @@ -61,11 +65,11 @@ import Primer.Eval.Redex (
Dir (..),
EvalLog (..),
MonadEval,
getNonCapturedLocal,
lookupEnclosingLet,
runRedex,
runRedexTy,
viewRedex,
viewRedexType,
viewRedexType, RunRedexOptions (RunRedexOptions,pushAndElide), ViewRedexOptions (ViewRedexOptions,aggressiveElision, pushMulti),
)
import Primer.Log (ConvertLogMessage)
import Primer.TypeDef (TypeDefMap)
Expand Down Expand Up @@ -101,8 +105,8 @@ step tydefs globals expr d i = runExceptT $ do
pure (expr', detail)

-- | Search for the given node by its ID.
-- Collect all local bindings in scope and return them
-- (with their local definition, if applicable)
-- Collect all immediately-surrounding let bindings and return them
-- (these are the ones we may push into this node)
-- along with the focused node.
-- Returns Nothing if the node is a binding, (note that no reduction rules can apply there).
findNodeByID :: ID -> Dir -> Expr -> Maybe (Cxt, Either (Dir, ExprZ) TypeZ)
Expand All @@ -111,10 +115,14 @@ findNodeByID i =
FMExpr
{ expr = \ez d c -> if getID ez == i then Just (c, Left (d, ez)) else Nothing
, ty = \tz c -> if getID tz == i then Just (c, Right tz) else Nothing
, subst = Nothing
, substTy = Nothing
}

-- We hardcode a permissive set of options for the interactive eval
-- (i.e. these see more redexes)
evalOpts :: ViewRedexOptions
evalOpts = ViewRedexOptions {pushMulti = True
, aggressiveElision = True}

-- | Return the IDs of nodes which are reducible.
-- We assume that the expression is well scoped. There are no
-- guarantees about whether we will claim that an ill-sorted variable
Expand All @@ -131,17 +139,19 @@ redexes tydefs globals =
(ListT.toList .)
. foldMapExpr
FMExpr
{ expr = \ez d -> liftMaybeT . runReaderT (getID ez <$ viewRedex tydefs globals d (target ez))
, ty = \tz -> runReader (whenJust (getID tz) <$> viewRedexType (target tz))
, subst = Nothing
, substTy = Nothing
{ expr = \ez d -> liftMaybeT . runReaderT (getID ez <$ viewRedex evalOpts tydefs globals d (target ez))
, ty = \tz -> runReader (whenJust (getID tz) <$> viewRedexType evalOpts (target tz))
}
where
liftMaybeT :: Monad m' => MaybeT m' a -> ListT m' a
liftMaybeT m = ListT $ fmap (,mempty) <$> runMaybeT m
-- whenJust :: Alternative f => a -> Maybe b -> f a
whenJust = maybe empty . const . pure

-- We hardcode a particular set of reduction options for the interactive evaluator
reductionOpts :: RunRedexOptions
reductionOpts = RunRedexOptions {pushAndElide = True}

-- | Given a context of local and global variables and an expression, try to reduce that expression.
-- Expects that the expression is redex and will throw an error if not.
tryReduceExpr ::
Expand All @@ -154,8 +164,8 @@ tryReduceExpr ::
Expr ->
m (Expr, EvalDetail)
tryReduceExpr tydefs globals cxt dir expr =
runMaybeT (flip runReaderT cxt $ viewRedex tydefs globals dir expr) >>= \case
Just r -> runRedex r
runMaybeT (flip runReaderT cxt $ viewRedex evalOpts tydefs globals dir expr) >>= \case
Just r -> runRedex reductionOpts r
_ -> throwError NotRedex

tryReduceType ::
Expand All @@ -167,6 +177,6 @@ tryReduceType ::
Type ->
m (Type, EvalDetail)
tryReduceType _globals cxt =
flip runReader cxt . viewRedexType <&> \case
Just r -> runRedexTy r
flip runReader cxt . viewRedexType evalOpts <&> \case
Just r -> runRedexTy reductionOpts r
_ -> throwError NotRedex
5 changes: 5 additions & 0 deletions primer/src/Primer/Eval/Detail.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Primer.Eval.Detail (
module Case,
module Let,
module Prim,
module Push,
) where

import Foreword
Expand All @@ -23,6 +24,7 @@ import Primer.Eval.Case as Case
import Primer.Eval.Inline as Inline
import Primer.Eval.Let as Let
import Primer.Eval.Prim as Prim
import Primer.Eval.Push as Push
import Primer.JSON (CustomJSON (CustomJSON), FromJSON, PrimerJSON, ToJSON)

-- | Detailed information about a reduction step
Expand All @@ -41,6 +43,9 @@ data EvalDetail
LetRemoval (LetRemovalDetail Expr)
| -- | Removing a type-level @let@ whose bound variable is unused
TLetRemoval (LetRemovalDetail Type)
| -- | Explicit-substitution style pushing a 'let' down the tree
PushLetDown (PushLetDetail Expr)
| PushLetDownTy (PushLetDetail Type)
| -- | Renaming of binding in an expression
BindRename (BindRenameDetail Expr)
| -- | Renaming of binding in a type
Expand Down
26 changes: 4 additions & 22 deletions primer/src/Primer/Eval/Let.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,13 @@

module Primer.Eval.Let (
LetRemovalDetail (..),
findFreeOccurrencesExpr,
findFreeOccurrencesType,
) where

import Foreword

import Control.Arrow ((***))
import Optics (filtered, getting, to, (%), (^..), _1)
import Primer.Core (
Expr,
ID,
LocalName (unLocalName),
TyVarName,
Type,
getID,
)
import Primer.Core.Utils (_freeVars, _freeVarsTy)
import Primer.JSON (CustomJSON (CustomJSON), FromJSON, PrimerJSON, ToJSON)
import Primer.Name (Name)

Expand All @@ -32,20 +22,12 @@ data LetRemovalDetail t = LetRemovalDetail
-- ^ the let expression before reduction
, after :: t
-- ^ the resulting expression after reduction
, bindingName :: Name
-- ^ the name of the unused bound variable (either term or type variable)
, letID :: ID
-- ^ the full let expression
, bindingNames :: NonEmpty Name
-- ^ the names of the unused bound variables (either term or type variable)
, letIDs :: NonEmpty ID
-- ^ the dropped let expressions
, bodyID :: ID
-- ^ the right hand side of the let
}
deriving stock (Eq, Show, Read, Generic)
deriving (FromJSON, ToJSON) via PrimerJSON (LetRemovalDetail t)

findFreeOccurrencesExpr :: LocalName k -> Expr -> [ID]
findFreeOccurrencesExpr x e = e ^.. _freeVars % to idName % filtered ((== unLocalName x) . snd) % _1
where
idName = either (getID *** unLocalName) (getID *** unLocalName)

findFreeOccurrencesType :: TyVarName -> Type -> [ID]
findFreeOccurrencesType x ty = ty ^.. getting _freeVarsTy % to (first getID) % filtered ((== x) . snd) % _1
Loading