+{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE QuantifiedConstraints #-}
+-- For Syn.LetRecable
+{-# LANGUAGE UndecidableInstances #-}
+
+module Symantic.Semantics.LetInserter where
+
+import Control.Applicative (liftA2, liftA3)
+import Control.Monad (Monad (..))
+import Control.Monad.Trans.State.Strict qualified as MT
+import Data.Bool (Bool (..), not, otherwise, (&&))
+import Data.Either (Either (..))
+import Data.Eq (Eq (..))
+import Data.Foldable (foldMap)
+import Data.Function (const, id, on, ($), (.))
+import Data.Functor ((<$>))
+import Data.Int (Int)
+import Data.Kind (Type)
+import Data.List qualified as List
+import Data.Maybe (Maybe (..), isNothing, maybe)
+import Data.Ord (Ord (..))
+import Data.Semigroup (Semigroup (..))
+import Data.Set qualified as Set
+import Debug.Trace qualified as Trace
+import Numeric.Natural (Natural)
+import Symantic.Syntaxes.Classes qualified as Syn
+import Symantic.Syntaxes.Derive qualified as Syn
+import Text.Show (Show (..), showListWith, showString, shows)
+import Unsafe.Coerce (unsafeCoerce)
+import Prelude (error, pred)
+import Prelude qualified
+
+-- import Control.Monad.Trans.Class qualified as MT
+-- import Data.Functor.Identity (Identity(..))
+-- import Data.Monoid (Monoid(..))
+-- import Data.String (String)
+
+traceShow :: Show a => a -> b -> b
+-- traceShow = Trace.traceShow
+traceShow _x a = a
+
+newtype Locus = Locus Natural
+ deriving (Eq, Ord)
+ deriving newtype (Show)
+
+{-
+type Locus = [Pigit]
+data Pigit = POne | PTwo | PThree | PLoc Locus | PNote String
+ deriving (Eq, Ord)
+instance Show Pigit where
+ showsPrec _p x =
+ case x of
+ POne -> shows (1::Int)
+ PTwo -> shows (2::Int)
+ PThree -> shows (3::Int)
+ PLoc loc -> shows loc
+ PNote msg -> showString msg
+-}
+
+-- * Type 'OpenCode'
+newtype OpenCode code a = OpenCode {unOpenCode :: Env code -> code a}
+type instance Syn.Derived (OpenCode code) = code
+
+-- instance Syn.Derivable (OpenCode code) where
+-- derive oc = unOpenCode oc mempty
+instance Syn.LiftDerived (OpenCode code) where
+ liftDerived x = OpenCode (const x)
+instance Syn.LiftDerived1 (OpenCode code) where
+ liftDerived1 f x = OpenCode (\env -> f (unOpenCode x env))
+instance Syn.LiftDerived2 (OpenCode code) where
+ liftDerived2 f x y = OpenCode (\env -> f (unOpenCode x env) (unOpenCode y env))
+instance Syn.LiftDerived3 (OpenCode code) where
+ liftDerived3 f x y z = OpenCode (\env -> f (unOpenCode x env) (unOpenCode y env) (unOpenCode z env))
+
+-- instance Syn.Abstractable code => Syn.Abstractable (OpenCode code) where
+-- lam f = OpenCode (\env -> Syn.lam (\x -> unOpenCode f env (unOpenCode x env)))
+-- instance Syn.Instantiable code => Syn.Instantiable (OpenCode code)
+-- instance Syn.Constantable c code => Syn.Constantable c (OpenCode code)
+instance Syn.IfThenElseable code => Syn.IfThenElseable (OpenCode code) where
+ ifThenElse x y z = OpenCode (\env -> Syn.ifThenElse (unOpenCode x env) (unOpenCode y env) (unOpenCode z env))
+instance Syn.Equalable code => Syn.Equalable (OpenCode code) where
+ equal = OpenCode (const Syn.equal)
+instance Syn.Constantable c code => Syn.Constantable c (OpenCode code)
+
+-- ** Type Env'
+type Env code = [EnvBind code]
+
+-- *** Type 'Var'
+newtype Var a = Var {unVar :: VarName}
+ deriving (Eq)
+ deriving newtype (Show)
+type VarName = Locus
+
+-- *** Type 'EnvBind'
+data EnvBind code where
+ EnvBind :: !(Var a, code a) -> EnvBind code
+instance Show (EnvBind code) where
+ showsPrec p (EnvBind (n, _e)) = showsPrec p n
+
+lookupVar :: Var a -> OpenCode code a
+lookupVar (Var v) =
+ traceShow (["lookupVar", "OpenCode"], v) $
+ OpenCode $ \env ->
+ traceShow (["lookupVar", "OpenCode", "find"], v, ("env", env)) $
+ case List.find (\(EnvBind (Var v1, _)) -> v1 == v) env of
+ Just (EnvBind (_, x)) -> unsafeCoerce x
+ Nothing -> error ("lookupVar failure for var=(" <> show v <> ") env=" <> show ((\(EnvBind (Var n, _v)) -> n) <$> env))
+
+lookupVarLetInserter :: Var a -> LetInserter code a
+lookupVarLetInserter v =
+ LetInserter $
+ return
+ AnnotatedCode
+ { annotatedCodeOpenCode = lookupVar v
+ , annotatedCodeFloatingLets = FloatingLets []
+ , annotatedCodeFreeVars = Set.singleton (unVar v)
+ }
+
+insertVar :: Var a -> code a -> Env code -> Env code
+insertVar v e env =
+ traceShow ("insertVar", v) $
+ EnvBind (v, e) : env
+
+-- * Type 'Memo'
+type Memo = Natural
+
+-- ** Type 'MaybeMemo'
+
+-- | Like a @(Maybe Memo)@ except 'Nothing' is never equal to any other value
+-- to implement unique memoization key.
+newtype MaybeMemo = MaybeMemo (Maybe Memo)
+ deriving (Show)
+
+instance Eq MaybeMemo where
+ MaybeMemo (Just x) == MaybeMemo (Just y) = x == y
+ _ == _ = False
+
+-- * Type 'FloatingLet'
+data FloatingLet code where
+ FloatingLet ::
+ { floatingLetLocus :: !(Maybe Locus)
+ -- ^ 'FloatingLet' are either inserted at the place of an explicit 'Locus',
+ -- or at the binding ('lam' or 'let_') that dominates all free variables of the bound expression,
+ -- whichever has the narrowest scope.
+ , floatingLetMemo :: !MaybeMemo
+ -- ^ Memoization key, either internal (unique) or user-specified.
+ --
+ -- The memo key defines the equivalence classes:
+ -- expressions with the same memo key are to be shared.
+ -- Therefore, if @('genLetMemo' k l e)@ finds that there is already a let-binding
+ -- produced by an earlier 'genLetMemo' with the same locus @(l)@ and the same memo key @(k)@,
+ -- @('genLetMemo' k l e) returns the code of the earlier bound variable.
+ , floatingLetVarName :: !(Var a)
+ , floatingLetBoundCode :: !(BoundCode code a)
+ , floatingLetFreeVars :: !(Set.Set VarName)
+ } ->
+ FloatingLet code
+instance Show (FloatingLet code) where
+ showsPrec _p FloatingLet{..} =
+ showString "FloatingLet{"
+ . showString "locus="
+ . shows floatingLetLocus
+ . showString ", "
+ . showString "memo="
+ . shows floatingLetMemo
+ . showString ", "
+ . showString "var="
+ . shows floatingLetVarName
+ . showString ", "
+ . showString "exp="
+ . showString (case floatingLetBoundCode of BoundCodeOpenCode{} -> "BoundCodeOpenCode"; BoundCodeLetInserter{} -> "BoundCodeLetInserter")
+ . showString ", "
+ . showString "fvs="
+ . showListWith (shows) (Set.toList floatingLetFreeVars)
+ . showString ", "
+ . showString "}"
+
+-- * Type 'VLBindings'
+
+-- A virtual let-binding.
+-- The key idea is virtual let-bindings: whereas clet introduces an ordinary let-binding
+-- whose location is fixed, glet generates the code for a fresh variable accompanied by a virtual
+-- binding of that variable. Virtual bindings do not have (yet) a fixed location: they are
+-- attached to the expression that uses their bound variables and ‘float up’ when their attached
+-- expression is incorporated into a bigger expression. Eventually, when they reach a point that
+-- a metaprogrammer has marked with a dedicated primitive, virtual bindings are converted to
+-- real let-bindings.
+newtype FloatingLets code = FloatingLets {unFloatingLets :: [FloatingLet code]}
+instance Show (FloatingLets code) where
+ showsPrec _p (FloatingLets l) = go l
+ where
+ go [] = showString "\n"
+ go (e : es) = showString "\n , " . shows e . go es
+
+-- | Append two lists skipping the duplicates.
+-- The order of elements is preserved
+mergeFloatingLets :: FloatingLets code -> FloatingLets code -> FloatingLets code
+mergeFloatingLets (FloatingLets xs) (FloatingLets ys) =
+ traceShow (["mergeFloatingLets"], xs, ys) $
+ FloatingLets (loop (List.reverse xs) ys)
+ where
+ loop acc = \case
+ [] -> List.reverse acc
+ y@FloatingLet{floatingLetVarName = Var yv} : t ->
+ case List.filter
+ ( \a@FloatingLet{floatingLetVarName = Var av} ->
+ floatingLetLocus y == floatingLetLocus a
+ && floatingLetMemo y == floatingLetMemo a
+ && yv == av
+ )
+ acc of
+ [] -> loop (y : acc) t
+ _ -> loop (acc) t
+
+-- fs ->
+-- loop (List.concatMap (\FloatingLet{floatingLetBoundCode, floatingLetLocus, floatingLetMemo, floatingLetVarName=Var av} ->
+-- if av == yv
+-- then
+-- FloatingLet { floatingLetVarName=Var yv, .. }) fs <> acc) t
+instance Semigroup (FloatingLets code) where
+ (<>) = mergeFloatingLets
+
+-- instance Monoid (FloatingLets code) where
+-- mempty = FloatingLets []
+-- mappend = (<>)
+
+-- * Type 'BoundCode'
+
+-- | Bound expression
+data BoundCode (code :: Type -> Type) a
+ = BoundCodeOpenCode !(OpenCode code a)
+ | -- | In the recursive case,
+ -- a virtual binding may carry a yet to be computed piece of code.
+ BoundCodeLetInserter !(LetInserter code a)
+
+-- * Type 'AnnotatedCode'
+
+-- | A code value.
+-- A code value is annotated only with the type of the expression it generates,
+-- with no further classifiers (at present).
+data AnnotatedCode (code :: Type -> Type) a = AnnotatedCode
+ { annotatedCodeOpenCode :: !(OpenCode code a)
+ , annotatedCodeFloatingLets :: !(FloatingLets code)
+ , annotatedCodeFreeVars :: !(Set.Set VarName)
+ -- ^ The free variables of the code,
+ -- used to limit the floating up of locus-less 'FloatingLet's
+ -- at 'lam' or 'let_' level, in order to avoid scope extrusion.
+ }
+
+-- annotatedCodeFreeVars :: AnnotatedCode code a -> Set.Set VarName
+-- annotatedCodeFreeVars annotatedCode = foldMap floatingLetFreeVars (unFloatingLets (annotatedCodeFloatingLets annotatedCode))
+
+runCD :: Syn.Letable code => AnnotatedCode code a -> code a
+runCD AnnotatedCode{annotatedCodeOpenCode = oc, annotatedCodeFloatingLets = FloatingLets bindingFLS} =
+ let bindingLFSByMemo = groupBy ((==) `on` floatingLetMemo) bindingFLS
+ in (`unOpenCode` []) $ List.foldr bindFloatingLetMemo oc bindingLFSByMemo
+runCD _ = error "runCD: virtual binding extrusion"
+
+type instance Syn.Derived (AnnotatedCode code) = OpenCode code
+instance Syn.LiftDerived (AnnotatedCode code) where
+ liftDerived x = AnnotatedCode{annotatedCodeOpenCode = x, annotatedCodeFloatingLets = FloatingLets [], annotatedCodeFreeVars = Set.empty}
+instance Syn.LiftDerived1 (AnnotatedCode code) where
+ liftDerived1 f e1 =
+ AnnotatedCode
+ { annotatedCodeOpenCode = f (annotatedCodeOpenCode e1)
+ , annotatedCodeFloatingLets = annotatedCodeFloatingLets e1
+ , annotatedCodeFreeVars = annotatedCodeFreeVars e1
+ }
+instance Syn.LiftDerived2 (AnnotatedCode code) where
+ liftDerived2 f e1 e2 =
+ AnnotatedCode
+ { annotatedCodeOpenCode = f (annotatedCodeOpenCode e1) (annotatedCodeOpenCode e2)
+ , annotatedCodeFloatingLets = mergeFloatingLets (annotatedCodeFloatingLets e1) (annotatedCodeFloatingLets e2)
+ , annotatedCodeFreeVars = annotatedCodeFreeVars e1 <> annotatedCodeFreeVars e2
+ }
+instance Syn.LiftDerived3 (AnnotatedCode code) where
+ liftDerived3 f e1 e2 e3 =
+ AnnotatedCode
+ { annotatedCodeOpenCode = f (annotatedCodeOpenCode e1) (annotatedCodeOpenCode e2) (annotatedCodeOpenCode e3)
+ , annotatedCodeFloatingLets = mergeFloatingLets (annotatedCodeFloatingLets e1) (mergeFloatingLets (annotatedCodeFloatingLets e2) (annotatedCodeFloatingLets e3))
+ , annotatedCodeFreeVars = annotatedCodeFreeVars e1 <> annotatedCodeFreeVars e2 <> annotatedCodeFreeVars e3
+ }
+instance Syn.Constantable c code => Syn.Constantable c (AnnotatedCode code)
+instance Syn.Instantiable code => Syn.Instantiable (AnnotatedCode code)
+instance Syn.IfThenElseable code => Syn.IfThenElseable (AnnotatedCode code)
+instance Syn.Equalable code => Syn.Equalable (AnnotatedCode code)
+
+-- * Type 'LetInserter'
+newtype LetInserter code a = LetInserter {unR :: MT.State Natural (AnnotatedCode code a)}
+runLetInserter :: Syn.Letable code => LetInserter code a -> code a
+runLetInserter = runCD . (`MT.evalState` 0) . unR
+
+type instance Syn.Derived (LetInserter code) = AnnotatedCode code
+instance Syn.LiftDerived (LetInserter sem) where
+ liftDerived = LetInserter . return
+instance Syn.LiftDerived1 (LetInserter sem) where
+ liftDerived1 f e1 = LetInserter (f <$> unR e1)
+instance Syn.LiftDerived2 (LetInserter sem) where
+ liftDerived2 f e1 e2 = LetInserter (liftA2 f (unR e1) (unR e2))
+instance Syn.LiftDerived3 (LetInserter sem) where
+ liftDerived3 f e1 e2 e3 = LetInserter (liftA3 f (unR e1) (unR e2) (unR e3))
+instance Syn.IfThenElseable code => Syn.IfThenElseable (LetInserter code)
+instance Syn.Equalable code => Syn.Equalable (LetInserter code)
+instance Syn.Constantable c code => Syn.Constantable c (LetInserter code)
+instance Syn.Instantiable code => Syn.Instantiable (OpenCode code)
+
+freshNatural :: MT.State Natural Natural
+freshNatural = do
+ n <- MT.get
+ MT.put (Prelude.succ n)
+ return n
+
+-- type instance Syn.Derived (LetInserter sem) = Syn.Derived sem
+freshVar :: MT.State Natural (Var a)
+freshVar = Var . Locus <$> freshNatural
+
+-- instance Syn.Constantable c sem => Syn.Constantable c (LetInserter sem) where
+-- constant c = LetInserter $ pure $ Syn.constant c
+instance Syn.Instantiable sem => Syn.Instantiable (LetInserter sem)
+
+-- f .@ x = LetInserter $ Syn.liftDerived2 (Syn..@) (unR f) (unR x)
+-- instance Syn.Abstractable sem => Syn.Abstractable (LetInserter sem) where
+-- lam f = LetInserter $ \loc ->
+-- Syn.lam $ \x ->
+-- unR (f (LetInserter (\_loc -> x)))
+-- (PNote "lam":loc)
+-- instance Syn.Abstractable code => Syn.Abstractable (LetInserter (AnnotatedCode code)) where
+-- lam body = LetInserter $ \loc ->
+-- AnnotatedCode
+-- { annotatedCodeOpenCode = _e
+-- }
+-- -- \x -> unR (body (LetInserter (\_ -> x))) (PNote "lam":loc)
+-- instance Syn.Letable (LetInserter sem) where
+-- let_ exp body = LetInserter $ \loc ->
+-- let x = unR exp (POne:loc) in
+-- unR (body (LetInserter (\_loc -> x))) (PTwo:loc)
+
+-- instance Syn.Letable code => Syn.Letable (LetInserter code) where
+-- let_ e body = LetInserter $ \loc ->
+-- let x = unR e (POne:loc) in
+-- unR (body (LetInserter (\_loc -> x))) (PTwo:loc)
+
+-- instance Syn.Letable (LetInserter sem) where
+-- let_ exp body = LetInserter (\loc -> let x = unR exp (POne:loc) in unR (body (LetInserter (\_loc -> x))) (PTwo:loc))
+-- instance Syn.IfThenElseable code => Syn.IfThenElseable (LetInserter (AnnotatedCode code)) where
+-- ifThenElse =
+-- Syn.liftDerived3 $ \bC okC koC -> OpenCode $ \env ->
+-- Syn.ifThenElse (unOpenCode bC env) (unOpenCode okC env) (unOpenCode koC env)
+-- instance Syn.FromDerived Syn.Equalable sem => Syn.Equalable (LetInserter sem) where
+-- equal = LetInserter (\loc -> trace ("equal "<>show loc) $ Syn.equal)
+
+-- | Check to see there are no duplicate 'VarName's in 'VLBindings'.
+-- This is just a safety check
+check_no_dup_varname :: FloatingLets code -> a -> a
+check_no_dup_varname = loop []
+ where
+ loop :: [VarName] -> FloatingLets code -> a -> a
+ loop _found (FloatingLets []) = id
+ loop found (FloatingLets (FloatingLet{floatingLetVarName = Var v, ..} : t))
+ | v `List.elem` found =
+ error ("Duplicate varname " <> show v <> ", with locus " <> show floatingLetLocus)
+ | otherwise = loop (v : found) (FloatingLets t)
+
+-- | let-bind all vlbindings with the given locus.
+-- This is a non-recursive case. All these let-bindings are independent
+-- It is up to the user to ensure the independence: if the bindings
+-- are dependent, the user should have introduced several loci
+-- Virtual let bindings are sort of free variables.
+-- We bind free variables later. Likewise, we bind let-bindings later.
+-- 'Memo' is ignored here.
+-- class GenLetable sem where
+-- instance Syn.Letable code => GenLetable (LetInserter (AnnotatedCode code)) where
+floatingLetVarname :: FloatingLet code -> VarName
+floatingLetVarname FloatingLet{floatingLetVarName = Var v} = v
+
+instance
+ ( Syn.Abstractable code
+ , Syn.Letable code
+ , Syn.LetRecable Int code
+ ) =>
+ Syn.Abstractable (LetInserter code)
+ where
+ lam body = LetInserter $ do
+ vname <- freshVar
+ unR $
+ Syn.liftDerived1
+ ( \bodyCD ->
+ let (bindingFLS, passingFLS) =
+ List.partition
+ (\fl -> Set.member (unVar vname) (floatingLetFreeVars fl))
+ (unFloatingLets (annotatedCodeFloatingLets bodyCD))
+ in let bindingLFSByMemo = groupBy ((==) `on` floatingLetMemo) bindingFLS
+ in traceShow
+ ( "lam"
+ , ("vname", unVar vname)
+ , ("bindingFLS", FloatingLets bindingFLS)
+ , ("passingFLS", FloatingLets passingFLS)
+ , ("bodyFreeVars", annotatedCodeFreeVars bodyCD)
+ , ("annotatedCodeFloatingLets bodyCD", annotatedCodeFloatingLets bodyCD)
+ )
+ $ Syn.liftDerived1
+ ( \bodyOC ->
+ OpenCode $ \env ->
+ Syn.lam $ \x ->
+ (`unOpenCode` insertVar vname x env) $
+ List.foldr bindFloatingLetMemo bodyOC bindingLFSByMemo
+ )
+ bodyCD
+ { annotatedCodeFloatingLets = FloatingLets passingFLS
+ , annotatedCodeFreeVars =
+ Set.delete (unVar vname) $
+ annotatedCodeFreeVars bodyCD
+ `Set.difference` Set.fromList (floatingLetVarname <$> bindingFLS)
+ }
+ )
+ (body (lookupVarLetInserter vname))
+instance
+ ( Syn.Letable code
+ , Syn.LetRecable Int code
+ ) =>
+ Syn.Letable (LetInserter code)
+ where
+ let_ expR body = LetInserter $ do
+ vname <- freshVar
+ unR $
+ Syn.liftDerived2
+ ( \expCD bodyCD ->
+ let (bindingFLS, passingFLS) =
+ List.partition
+ (\fl -> Set.member (unVar vname) (floatingLetFreeVars fl))
+ (unFloatingLets (annotatedCodeFloatingLets bodyCD))
+ in let bindingLFSByMemo = groupBy ((==) `on` floatingLetMemo) bindingFLS
+ in Syn.liftDerived2
+ ( \expOC bodyOC ->
+ OpenCode $ \env ->
+ Syn.let_ (unOpenCode expOC env) $ \x ->
+ (`unOpenCode` (insertVar vname x env)) $
+ List.foldr bindFloatingLetMemo bodyOC bindingLFSByMemo
+ )
+ expCD
+ bodyCD
+ { annotatedCodeFloatingLets = FloatingLets passingFLS
+ , annotatedCodeFreeVars =
+ Set.delete (unVar vname) $
+ annotatedCodeFreeVars bodyCD
+ `Set.difference` Set.fromList (floatingLetVarname <$> bindingFLS)
+ }
+ )
+ expR
+ (body (lookupVarLetInserter vname))
+
+bindLet :: Syn.Letable code => FloatingLet code -> OpenCode code a -> OpenCode code a
+bindLet fl2@FloatingLet{..} c =
+ traceShow ("bindLet", fl2) $
+ case floatingLetBoundCode of
+ BoundCodeOpenCode oc -> OpenCode $ \env ->
+ Syn.let_ (unOpenCode oc env) $ \x ->
+ unOpenCode c $
+ -- traceShow ("bindLet", floatingLetVarName) $
+ insertVar floatingLetVarName x env
+ _ -> error "bindLet is always called for evaluated bindings, without latent bound exp"
+
+-- instance Syn.LetRecable idx (LetInserter sem) where
+-- letRec _idx f body =
+-- let self idx = LetInserter (\loc ->
+-- --trace ("letRec "<>show loc) $
+-- unR (f self idx) (PNote "fix":loc))
+-- in body self
+
+-- mletRec :: p -> ((t1 -> LetInserter sem a) -> t1 -> LetInserter sem a) -> ((t1 -> LetInserter sem a) -> t2) -> t2
+-- mletRec _idx f body =
+-- let self idx = LetInserter $
+-- --trace ("letRec "<>show loc) $
+-- unR (f self idx)
+-- in body self
+--
+-- letR ::
+-- Syn.LetRecable Int sem =>
+-- (sem a -> sem a) ->
+-- (sem a -> sem w) ->
+-- sem w
+-- letR f body = Syn.letRec (1::Int)
+-- (\self _idx -> f (self 0))
+-- (\self -> body (self 0))
+-- type family MemoKey (sem:: Type -> Type) where MemoKey a = Memo
+
+-- | Memoizing non-recursive genLet
+class MemoGenLetable sem where
+ withLocus :: (Locus -> sem a) -> sem a
+ genLetMemoAtMaybe :: Maybe Memo -> Maybe Locus -> sem a -> sem a
+
+genLetMemo :: MemoGenLetable sem => Memo -> Locus -> sem a -> sem a
+genLetMemo m l = genLetMemoAtMaybe (Just m) (Just l)
+
+genLet :: MemoGenLetable sem => sem a -> sem a
+genLet = genLetMemoAtMaybe Nothing Nothing
+
+genLetAt :: MemoGenLetable sem => Locus -> sem a -> sem a
+genLetAt = genLetMemoAtMaybe Nothing . Just
+
+instance Syn.Letable code => MemoGenLetable (LetInserter code) where
+ withLocus body = LetInserter $ do
+ lname <- Locus <$> freshNatural
+ bodyCD <- unR (body lname)
+ let fls = annotatedCodeFloatingLets bodyCD
+ let bindingByLocusFLS = List.filter (\fl -> floatingLetLocus fl == Just lname) (unFloatingLets fls)
+ let bindingVars = foldMap (Set.singleton . floatingLetVarname) bindingByLocusFLS
+ let (bindingFLS, passingFLS) = List.partition (\fl -> not (Set.disjoint bindingVars (floatingLetFreeVars fl))) (unFloatingLets fls)
+ let bindingLFSByMemo = groupBy ((==) `on` floatingLetMemo) bindingFLS
+ return $
+ traceShow (["withLocus"], ("bindingLFSByMemo", bindingLFSByMemo)) $
+ -- check_no_dup_varname (annotatedCodeFloatingLets bodyCD) $
+ AnnotatedCode
+ { annotatedCodeOpenCode = List.foldr bindFloatingLetMemo (annotatedCodeOpenCode bodyCD) bindingLFSByMemo
+ , annotatedCodeFloatingLets = FloatingLets passingFLS
+ , annotatedCodeFreeVars = annotatedCodeFreeVars bodyCD `Set.difference` Set.fromList (floatingLetVarname <$> bindingFLS)
+ }
+ genLetMemoAtMaybe keyMaybe mlname body = LetInserter $ traceShow (["genLetMemoAtMaybe"]) $ do
+ vname <- freshVar
+ -- mkey <- runIdentity <$> unR key
+ bodyCD <-
+ traceShow ("genLetMemoAtMaybe", ("vname", vname), ("key", keyMaybe)) $
+ unR body
+ let fvs = Set.insert (unVar vname) (annotatedCodeFreeVars bodyCD)
+ -- if the bodyression to let-bind has itself let-bindings,
+ -- they are straightened out
+ -- The new binding is added at the end; it may depend on
+ -- the bindings in evl (as is the case for the nested genLet)
+ return $
+ AnnotatedCode
+ { annotatedCodeOpenCode = lookupVar vname
+ , annotatedCodeFloatingLets =
+ FloatingLets $
+ unFloatingLets (annotatedCodeFloatingLets bodyCD)
+ <> [ FloatingLet
+ { floatingLetLocus = mlname
+ , floatingLetMemo = MaybeMemo keyMaybe
+ , floatingLetVarName = vname
+ , floatingLetBoundCode = BoundCodeOpenCode (annotatedCodeOpenCode bodyCD)
+ , floatingLetFreeVars = fvs
+ }
+ ]
+ , annotatedCodeFreeVars = fvs
+ }
+
+-- Bind the variables in order, preserving the dependency.
+-- Bind only one variable within each group, substituting the others
+bindFloatingLetMemo :: Syn.Letable code => [FloatingLet code] -> OpenCode code a -> OpenCode code a
+bindFloatingLetMemo gr oc =
+ traceShow (["withLocus", "bindFloatingLetMemo"]) $
+ case assertSameLocuses gr of
+ [] -> error "bindFloatingLetMemo: empty group"
+ [fl] -> bindLet fl oc
+ fl@FloatingLet{floatingLetVarName = vName} : fls ->
+ bindLet fl $ OpenCode $ \env ->
+ let defCode = unOpenCode (lookupVar vName) env
+ in let ext_equ env' fl2 =
+ reBind2 (memKey fl) defCode fl2
+ : env'
+ in unOpenCode oc $ List.foldl' ext_equ env fls
+ where
+ memKey fl@FloatingLet{floatingLetBoundCode = BoundCodeOpenCode{}} = floatingLetMemo fl
+ memKey fl = error $ "group pust be canonical: " <> show fl
+
+assertSameLocuses :: [FloatingLet code] -> [FloatingLet code]
+assertSameLocuses fls
+ | Set.size (foldMap (Set.singleton . floatingLetLocus) fls) <= 1 = traceShow ("assertSameLocuses", fls) fls
+ | otherwise = error "assertSameLocuses"
+
+-- | Especially for 'Locus', all bindings have the same type.
+-- In any event, 'Locus' plus the 'Memo' key witness for the bindings' type.
+reBind2 :: MaybeMemo -> code a -> FloatingLet sem -> EnvBind code
+reBind2 k c fl
+ | k == floatingLetMemo fl =
+ traceShow (["reBind2"], ("k", k), ("fl", fl)) $
+ EnvBind (Var (floatingLetVarname fl), c)
+reBind2 k _ fl = error $ "reBind2: locus/memo mismatch" <> show (("k", k), ("fl", fl))
+
+{-
+genLetAt :: LetInserter code a -> LetInserter code a
+genLetAt = LetInserter $
+ k <- freshKey
+ genLetMemo
+-}
+-- TODO: Here genlet is a version of genlet l em e described earlier, for the case of all memo keys being distinct.
+-- TODO: newtype LocusRec = Locus
+{-
+genLetM = LetInserter $ \loc ->
+ k <- freshKey
+ genLetMemo Nothing k
+-}
+
+-- | Unlike withLocus, we now have to evaluate expressions that
+-- may be present in virtual bindings.
+-- The rule: within an equivalence class, we evaluate only
+-- the bexp of its representative (the first member of the group)
+-- That evaluation may create more virtual bindings, which are to be merged
+
+-- Pick vlbindings for the given locus and convert them to
+
+-- * normal* equivalence classes.
+
+-- Return the list of normal equivalence classes and the
+-- remaining bindings (for different loci).
+-- An equivalence class is called normal if its representative
+-- (the first binding in the group) has the BoundCodeOpenCode as the bound
+-- expression (that is, it is determined which future-stage expression to bind).
+-- If a group is not normal, that is, its representative has a not
+-- yet determined bound expression, we have to determine it --
+-- which creates more bindings to normalize (some of which could be
+-- members of existing groups -- but never replace the existing
+-- representative -- we always preserve the order!)
+--
+-- Keeping the correct order and maintain dependency is actually
+-- very difficult. When evaluating the expression bound to var v,
+-- we obtain a code value with vlbindings. Some of them mention v
+-- (and so should be put into the same group as v, but _after_ it),
+-- some are new and so should be put in a group _before_ v because
+-- the v binding may depend on them.
+-- It seems simpler to just forget about the order between groups
+-- and dependencies and generate one big letrec with mutually dependent clauses.
+
+-- | @('groupBy' eq xs)@: partition a list @xs@
+-- into a list of equivalence classes quotiented by the @eq@ function
+--
+-- The order of the groups follows the order in the input list;
+-- within each group, the elements occur in the same order they
+-- do in the input list.
+groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
+groupBy eq = go []
+ where
+ go acc [] = List.reverse acc
+ go acc (x : xs) = go ((x : grp) : acc) rest
+ where
+ (grp, rest) = List.partition (eq x) xs
+
+normalizeGroup :: Locus -> FloatingLets code -> MT.State Natural ([FloatingLets code], FloatingLets code)
+normalizeGroup lname vl = do
+ let (bindingFLS, passingFLS) = List.partition (\FloatingLet{..} -> floatingLetLocus == Just lname) (unFloatingLets vl)
+ let bindingLFSByMemo = groupBy ((==) `on` floatingLetMemo) bindingFLS
+ -- Try to find an abnormal group
+ let (normalGroups, abnormalGroups) =
+ let abnomality_check = \case
+ FloatingLet{floatingLetBoundCode = BoundCodeLetInserter{}} : _ -> True
+ _ -> False
+ in List.break abnomality_check bindingLFSByMemo
+ -- traceShow (["normalizeGroup"], ("lname",lname), ("bindingLFSByMemo", FloatingLets <$> bindingLFSByMemo), ("normalGroups", FloatingLets <$> normalGroups), ("abnormalGroups", FloatingLets <$> abnormalGroups), ("passingFLS", FloatingLets passingFLS)) $
+ case abnormalGroups of
+ -- all groups are normal
+ [] ->
+ return $
+ traceShow (["normalizeGroup", "return"], ("lname", lname), ("normalGroups", FloatingLets <$> normalGroups), ("passingFLS", passingFLS)) $
+ (FloatingLets <$> normalGroups, FloatingLets passingFLS)
+ -- at least one group representative is abnormal
+ (fl0@FloatingLet{floatingLetBoundCode = BoundCodeLetInserter fl0Exp, floatingLetVarName = fl0VarName} : fls0) : grps -> do
+ fl0BodyCD <- unR fl0Exp
+ -- The group has become normal
+ let normalizedFL0 =
+ FloatingLet
+ { floatingLetLocus = floatingLetLocus fl0
+ , floatingLetMemo = floatingLetMemo fl0
+ , floatingLetVarName = fl0VarName
+ , floatingLetBoundCode = BoundCodeOpenCode (annotatedCodeOpenCode fl0BodyCD)
+ , -- Update the free variables, which could not be done in 'genLetMemoRec'
+ -- where fl0BodyCD was not yet available.
+ floatingLetFreeVars = floatingLetFreeVars fl0 <> annotatedCodeFreeVars fl0BodyCD
+ }
+ : fls0
+ let groups = normalGroups <> (normalizedFL0 : grps)
+ -- New bindings
+ -- let vl' = FloatingLets (List.concat groups) <> FloatingLets passingFLS <> annotatedCodeFloatingLets fl0BodyCD
+ let vl' = FloatingLets $ List.concat groups <> passingFLS <> unFloatingLets (annotatedCodeFloatingLets fl0BodyCD)
+ normalizeGroup lname vl'
+ _ -> error "normalizeGroup: groups are always non-empty"
+
+-- * Class 'MemoGenLetRecable'
+class MemoGenLetRecable sem where
+ withLocusRec :: (Locus -> sem a) -> sem a
+ genLetMemoRec :: Locus -> Memo -> sem a -> sem a
+
+instance
+ Syn.LetRecable Int code =>
+ MemoGenLetRecable (LetInserter code)
+ where
+ withLocusRec body = LetInserter $ do
+ lname <- Locus <$> freshNatural
+ bodyCD <- unR $ body lname
+ (bindingFLS, passingFLS) <-
+ check_no_dup_varname (annotatedCodeFloatingLets bodyCD) $
+ normalizeGroup lname (annotatedCodeFloatingLets bodyCD)
+ -- Bind the variables in order, preserving the dependency
+ return $
+ traceShow (["withLocusRec", "return"], ("lname", lname), ("bindingFLS", bindingFLS), ("passingFLS", passingFLS)) $
+ AnnotatedCode
+ { annotatedCodeOpenCode = bindFloatingLetMemoRec bindingFLS (annotatedCodeOpenCode bodyCD)
+ , annotatedCodeFloatingLets = passingFLS
+ , annotatedCodeFreeVars = annotatedCodeFreeVars bodyCD `Set.difference` foldMap (Set.fromList . (floatingLetVarname <$>) . unFloatingLets) bindingFLS
+ }
+ genLetMemoRec lname key body = LetInserter $ do
+ vname <- freshVar
+ -- Cannot compute all the free variables yet
+ -- because body is not yet evaluated to a 'AnnotatedCode'
+ let fvs = Set.singleton (unVar vname)
+ return $
+ traceShow (["genLetMemoRec"], ("key", key), ("vname", vname)) $
+ AnnotatedCode
+ { annotatedCodeOpenCode = lookupVar vname
+ , annotatedCodeFloatingLets =
+ FloatingLets
+ [ FloatingLet
+ { floatingLetLocus = Just lname
+ , floatingLetMemo = MaybeMemo (Just key)
+ , floatingLetVarName = vname
+ , floatingLetBoundCode = BoundCodeLetInserter body
+ , floatingLetFreeVars = fvs
+ }
+ ]
+ , annotatedCodeFreeVars = fvs
+ }
+
+-- | Create mutually-recursive letrec bindings.
+-- All variables are bound at the same time.
+-- The binding expression is taken from the group representative.
+-- It has to be 'BoundCodeOpenCode': all groups assumed to be normal.
+bindFloatingLetMemoRec ::
+ Syn.LetRecable Int code =>
+ [FloatingLets code] ->
+ OpenCode code a ->
+ OpenCode code a
+bindFloatingLetMemoRec [] c = c
+bindFloatingLetMemoRec bindingFLS c =
+ traceShow (["bindFloatingLetMemoRec", "OpenCode"], ("bindingFLS", bindingFLS)) $
+ OpenCode $ \env ->
+ traceShow (["bindFloatingLetMemoRec", "Syn.letRec"], ("bindingFLS", bindingFLS), ("env", env)) $
+ Syn.letRec
+ len
+ ( \self idx ->
+ traceShow (["bindFloatingLetMemoRec", "Syn.letRec", "binds"], ("idx", idx)) $
+ ( traceShow (["bindFloatingLetMemoRec", "Syn.letRec", "binds", "List.!!"], ("len", len), ("idx", idx), ("env", env)) $
+ unOpenCode (tobindOC List.!! idx)
+ )
+ ( traceShow (["bindFloatingLetMemoRec", "Syn.letRec", "binds", "bind_all"], ("idx", idx), ("env", env)) $
+ bind_all self env
+ )
+ )
+ ( \self ->
+ traceShow (["bindFloatingLetMemoRec", "Syn.letRec", "end"], ("env", env)) $
+ unOpenCode c (bind_all self env)
+ )
+ where
+ len = List.length bindingFLS
+ -- (label,key) of group representative
+ lk (fl@FloatingLet{floatingLetBoundCode = BoundCodeOpenCode{}} : _) = floatingLetMemo fl
+ lk _ = error "groups must be non-empty and canonical"
+ bind_group :: code a -> FloatingLets code -> [EnvBind code]
+ -- Create bindings to bind all identifiers in a group
+ bind_group ec (FloatingLets grp) =
+ traceShow (["bindFloatingLetMemoRec", "bind_group"], ("grp", grp)) $
+ List.map (reBind2 (lk grp) ec) (assertSameLocuses grp)
+ bind_all self env =
+ traceShow (["bindFloatingLetMemoRec", "bind_all"], ("bindingFLS", bindingFLS), ("env", env)) $
+ List.concat $
+ List.zipWith (\grp i -> bind_group (self i) grp) bindingFLS [0 :: Int .. pred len] <> [env]
+ tobindOC =
+ traceShow (["bindFloatingLetMemoRec", "tobindOC"], ("bindingFLS", bindingFLS)) $
+ List.map
+ ( \case
+ -- TODO: avoid unwrapping
+ FloatingLets (fl@FloatingLet{floatingLetBoundCode = BoundCodeOpenCode oc} : _) ->
+ traceShow (["bindFloatingLetMemoRec", "tobindOC", "map"], fl) $
+ unsafeCoerce (oc)
+ _ -> error "tobindOC"
+ )
+ bindingFLS
+
+{-
+genLetLocus :: Syn.Letable code => (Locus -> LetInserter code a) -> LetInserter code a
+genLetLocus body = LetInserter $ do
+ lname <- Locus <$> freshNatural
+ bodyCD <-
+ traceShow ("genLetLocus", ("lname", lname)) $
+ unR (body lname)
+ let (bindingFLS, passingFLS) =
+ List.partition (\fl -> floatingLetLocus fl == Just lname)
+ (unFloatingLets (annotatedCodeFloatingLets bodyCD))
+ -- Bind the variables in order, preserving the dependency
+ --traceShow ("genLetLocus", (bindingFLS, passingFLS)) $
+ return $
+ check_no_dup_varname (annotatedCodeFloatingLets bodyCD) $
+ AnnotatedCode{ annotatedCodeOpenCode = List.foldr bindLet (annotatedCodeOpenCode bodyCD) bindingFLS
+ , annotatedCodeFloatingLets = FloatingLets passingFLS
+ , annotatedCodeFreeVars = annotatedCodeFreeVars bodyCD `Set.difference` Set.fromList (floatingLetVarname <$> bindingFLS)
+ }
+-- In MetaOCaml, let(rec) is actually inserted either at the place
+-- of the explicit let locus,
+-- or at the binding that dominates all free variables of the bound expression,
+-- whichever has the narrowest scope.
+genLetAtMaybe :: Maybe Locus -> LetInserter code a -> LetInserter code a
+genLetAtMaybe mlname body = LetInserter $ do
+ vname <- freshVar
+ bodyCD <-
+ traceShow ("genLet", ("vname", vname)) $
+ unR body
+ -- if the expression to let-bind has itself let-bindings,
+ -- they are straightened out
+ -- The new binding is added at the end; it may depend on
+ -- the bindings coming before (as is the case for the nested genLet)
+ return $
+ traceShow (["genLet", "bodyCD"], ("vname", vname), ("bodyCDFreeVars", (annotatedCodeFreeVars bodyCD))) $
+ AnnotatedCode
+ { annotatedCodeOpenCode = traceShow (["genLet", "lookupVar"], ("vname", vname)) $ lookupVar vname
+ , annotatedCodeFloatingLets = FloatingLets $
+ --(\vls -> traceShow (["genLet", "bindings"], vls) vls) $
+ unFloatingLets (annotatedCodeFloatingLets bodyCD) <>
+ [ FloatingLet
+ { floatingLetLocus = mlname
+ , floatingLetMemo = MaybeMemo (Just 0)
+ , floatingLetVarName = vname
+ , floatingLetBoundCode = BoundCodeOpenCode (annotatedCodeOpenCode bodyCD)
+ , floatingLetFreeVars = Set.insert (unVar vname) (annotatedCodeFreeVars bodyCD)
+ }
+ ]
+ , annotatedCodeFreeVars = Set.insert (unVar vname) $ annotatedCodeFreeVars bodyCD
+ }
+genLetAt :: Locus -> LetInserter code a -> LetInserter code a
+genLetAt = genLetAtMaybe . Just
+
+genLet :: LetInserter code a -> LetInserter code a
+genLet = genLetAtMaybe Nothing
+-}