doc: fix old names
[haskell/symantic-parser.git] / src / Symantic / Parser / Grammar / Optimize.hs
index c5e3d42a0ac9e1c13852ac5e5848b1b4d5318cb4..cb6ee76524d4e67651eea7105b6bd06923f3de17 100644 (file)
@@ -1,12 +1,12 @@
 {-# LANGUAGE PatternSynonyms #-} -- For Comb
 {-# LANGUAGE TemplateHaskell #-} -- For branch
-{-# LANGUAGE ViewPatterns #-} -- For unSomeComb
+{-# LANGUAGE ViewPatterns #-} -- For unSimplComb
 {-# OPTIONS_GHC -fno-warn-orphans #-} -- For MakeLetName TH.Name
 -- | Bottom-up optimization of 'Comb'inators,
 -- reexamining downward as needed after each optimization.
 module Symantic.Parser.Grammar.Optimize where
 
-import Data.Bool (Bool(..))
+import Data.Bool (Bool(..), (&&), not)
 import Data.Either (Either(..), either)
 import Data.Eq (Eq(..))
 import Data.Function (($), (.))
@@ -14,18 +14,23 @@ import Data.Kind (Constraint)
 import Data.Maybe (Maybe(..))
 import Data.Set (Set)
 import Data.Functor.Identity (Identity(..))
+import Data.Functor.Product (Product(..))
+import Unsafe.Coerce (unsafeCoerce)
 import Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..))
+import Data.Semigroup (Semigroup(..))
 import qualified Data.Foldable as Foldable
-import qualified Data.Functor as Functor
-import qualified Data.List as List
+import qualified Data.Functor as F
+import qualified Data.HashMap.Strict as HM
+import qualified Data.HashSet as HS
+import Data.Hashable (Hashable)
+import qualified Language.Haskell.TH as TH
 
-import Symantic.Parser.Grammar.Combinators hiding (code)
-import qualified Symantic.Parser.Grammar.Production as Prod
+import Symantic.Parser.Grammar.Combinators
 import Symantic.Parser.Grammar.Production
-import Symantic.Univariant.Letable
-import Symantic.Univariant.Trans
-import qualified Symantic.Univariant.Lang as H
-import qualified Symantic.Univariant.Data as H
+import Symantic.Parser.Grammar.ObserveSharing
+import Symantic.Derive
+import qualified Symantic.Class as Prod
+import qualified Symantic.Data as Prod
 
 {-
 import Data.Function (($), flip)
@@ -34,14 +39,86 @@ import Debug.Trace (trace)
 (&) = flip ($)
 infix 0 &
 -}
+type OptimizeGrammar = KnotComb TH.Name
 
--- * Type 'OptimizeGrammar'
-type OptimizeGrammar = SomeComb
+-- | TODO: remove useless wrapping?
+newtype TiedComb repr a = TiedComb
+  {  combSimpl :: SimplComb repr a
+  --,  combRefs :: HS.HashSet letName
+  }
+
+-- * Type 'KnotComb'
+data KnotComb letName repr a = KnotComb
+  { knotCombOpens :: OpenRecs letName (SomeLet (TiedComb repr))
+    -- ^ 'TiedComb' for all 'letName' in 'lets'.
+  , knotCombOpen ::
+      LetRecs letName (SomeLet (TiedComb repr)) ->
+      TiedComb repr a
+    -- ^ 'TiedComb' of the current combinator,
+    -- with access to the final 'knotCombOpens'.
+  }
 
 optimizeGrammar ::
-  Trans (SomeComb repr) repr =>
-  SomeComb repr a -> repr a
-optimizeGrammar = trans
+  Derivable (SimplComb repr) =>
+  KnotComb TH.Name repr a -> repr a
+optimizeGrammar = derive . derive
+
+type instance Derived (KnotComb letName repr) = SimplComb repr
+instance Derivable (KnotComb letName repr) where
+  derive opt = combSimpl $
+    knotCombOpen opt (mutualFix (knotCombOpens opt))
+instance LiftDerived (KnotComb letName repr) where
+  liftDerived x = KnotComb
+    { knotCombOpens = HM.empty
+    , knotCombOpen = \_final -> TiedComb
+        { combSimpl = x
+        }
+    }
+instance LiftDerived1 (KnotComb letName repr) where
+  liftDerived1 f a = a
+    { knotCombOpen = \final -> TiedComb
+      { combSimpl = f (combSimpl (knotCombOpen a final))
+      }
+    }
+instance (Eq letName, Hashable letName) => LiftDerived2 (KnotComb letName repr) where
+  liftDerived2 f a b = KnotComb
+    { knotCombOpens = knotCombOpens a <> knotCombOpens b
+    , knotCombOpen = \final -> TiedComb
+      { combSimpl = f
+         (combSimpl (knotCombOpen a final))
+         (combSimpl (knotCombOpen b final))
+      }
+    }
+instance (Eq letName, Hashable letName) => LiftDerived3 (KnotComb letName repr) where
+  liftDerived3 f a b c = KnotComb
+    { knotCombOpens = HM.unions
+      [ knotCombOpens a
+      , knotCombOpens b
+      , knotCombOpens c
+      ]
+    , knotCombOpen = \final -> TiedComb
+      { combSimpl = f
+         (combSimpl (knotCombOpen a final))
+         (combSimpl (knotCombOpen b final))
+         (combSimpl (knotCombOpen c final))
+      }
+    }
+instance (Eq letName, Hashable letName) => LiftDerived4 (KnotComb letName repr) where
+  liftDerived4 f a b c d = KnotComb
+    { knotCombOpens = HM.unions
+      [ knotCombOpens a
+      , knotCombOpens b
+      , knotCombOpens c
+      , knotCombOpens d
+      ]
+    , knotCombOpen = \final -> TiedComb
+      { combSimpl = f
+         (combSimpl (knotCombOpen a final))
+         (combSimpl (knotCombOpen b final))
+         (combSimpl (knotCombOpen c final))
+         (combSimpl (knotCombOpen d final))
+      }
+    }
 
 -- * Data family 'Comb'
 -- | 'Comb'inators of the 'Grammar'.
@@ -49,65 +126,91 @@ optimizeGrammar = trans
 data family Comb
   (comb :: ReprComb -> Constraint)
   :: ReprComb -> ReprComb
+type instance Derived (Comb comb repr) = repr
 
--- | Convenient utility to pattern-match a 'SomeComb'.
-pattern Comb :: Typeable comb => Comb comb repr a -> SomeComb repr a
-pattern Comb x <- (unSomeComb -> Just x)
+-- | 'unsafeCoerce' restrained to 'SimplComb'.
+-- Useful to avoid dependant-map when inlining.
+unsafeSimplComb :: SimplComb repr a -> SimplComb repr b
+unsafeSimplComb = unsafeCoerce
 
--- ** Type 'SomeComb'
--- | Some 'Comb'inator existentialized over the actual combinator symantic class.
+-- | Convenient utility to pattern-match a 'SimplComb'.
+pattern Comb :: Typeable comb => Comb comb repr a -> SimplComb repr a
+pattern Comb x <- (unSimplComb -> Just x)
+
+-- ** Type 'SimplComb'
+-- | Interpreter simplifying combinators.
 -- Useful to handle a list of 'Comb'inators
 -- without requiring impredicative quantification.
 -- Must be used by pattern-matching
--- on the 'SomeComb' data-constructor,
+-- on the 'SimplComb' data-constructor,
 -- to bring the constraints in scope.
 --
 -- The optimizations are directly applied within it,
 -- to avoid introducing an extra newtype,
 -- this also give a more understandable code.
-data SomeComb repr a =
+data SimplComb repr a =
   forall comb.
-  (Trans (Comb comb repr) repr, Typeable comb) =>
-  SomeComb (Comb comb repr a)
+  (Derivable (Comb comb repr), Typeable comb) =>
+  SimplComb
+    { combData :: Comb comb repr a
+      -- ^ Some 'Comb'inator existentialized
+      -- over the actual combinator symantic class.
+    , combInline :: Bool
+      -- ^ Whether this combinator must be inlined
+      -- in place of a 'ref'erence pointing to it
+      -- (instead of generating a 'call').
+    , combRefs :: HS.HashSet TH.Name
+      -- ^ 'ref''s names reacheable from combinator
+      -- (including those behind 'ref's).
+    }
 
-instance Trans (SomeComb repr) repr where
-  trans (SomeComb x) = trans x
+type instance Derived (SimplComb repr) = repr
+instance Derivable (SimplComb repr) where
+  derive SimplComb{..} = derive combData
 
--- | @(unSomeComb c :: 'Maybe' ('Comb' comb repr a))@
--- extract the data-constructor from the given 'SomeComb'
+-- | @(unSimplComb c :: 'Maybe' ('Comb' comb repr a))@
+-- extract the data-constructor from the given 'SimplComb'
 -- iif. it belongs to the @('Comb' comb repr a)@ data-instance.
-unSomeComb ::
+unSimplComb ::
   forall comb repr a.
   Typeable comb =>
-  SomeComb repr a -> Maybe (Comb comb repr a)
-unSomeComb (SomeComb (c::Comb c repr a)) =
+  SimplComb repr a -> Maybe (Comb comb repr a)
+unSimplComb SimplComb{ combData = c :: Comb c repr a } =
   case typeRep @comb `eqTypeRep` typeRep @c of
     Just HRefl -> Just c
     Nothing -> Nothing
 
 -- CombAlternable
 data instance Comb CombAlternable repr a where
-  Alt :: Exception -> SomeComb repr a -> SomeComb repr a -> Comb CombAlternable repr a
+  Alt :: Exception -> SimplComb repr a -> SimplComb repr a -> Comb CombAlternable repr a
   Empty :: Comb CombAlternable repr a
   Failure :: SomeFailure -> Comb CombAlternable repr a
   Throw :: ExceptionLabel -> Comb CombAlternable repr a
-  Try :: SomeComb repr a -> Comb CombAlternable repr a
-instance CombAlternable repr => Trans (Comb CombAlternable repr) repr where
-  trans = \case
-    Alt exn x y -> alt exn (trans x) (trans y)
+  Try :: SimplComb repr a -> Comb CombAlternable repr a
+instance CombAlternable repr => Derivable (Comb CombAlternable repr) where
+  derive = \case
+    Alt exn x y -> alt exn (derive x) (derive y)
     Empty -> empty
     Failure sf -> failure sf
     Throw exn -> throw exn
-    Try x -> try (trans x)
+    Try x -> try (derive x)
 instance
   ( CombAlternable repr
   , CombApplicable repr
   , CombLookable repr
   , CombMatchable repr
   , CombSelectable repr
-  ) => CombAlternable (SomeComb repr) where
-  empty = SomeComb Empty
-  failure sf = SomeComb (Failure sf)
+  ) => CombAlternable (SimplComb repr) where
+  empty = SimplComb
+    { combData = Empty
+    , combInline = True
+    , combRefs = HS.empty
+    }
+  failure sf = SimplComb
+    { combData = Failure sf
+    , combInline = True
+    , combRefs = HS.empty
+    }
 
   alt _exn p@(Comb Pure{}) _ = p
     -- & trace "Left Catch Law"
@@ -121,53 +224,78 @@ instance
     -- & trace "Associativity Law"
   alt exn (Comb (Look p)) (Comb (Look q)) = look (alt exn (try p) q)
     -- & trace "Distributivity Law"
-  alt exn x y = SomeComb (Alt exn x y)
+  alt exn x y = SimplComb
+    { combData = Alt exn x y
+    , combInline = False
+    , combRefs = combRefs x <> combRefs y
+    }
 
-  throw exn = SomeComb (Throw exn)
+  throw exn = SimplComb
+    { combData = Throw exn
+    , combInline = True
+    , combRefs = HS.empty
+    }
 
   try (Comb (p :$>: x)) = try p $> x
     -- & trace "Try Interchange Law"
   try (Comb (f :<$>: p)) = f <$> try p
     -- & trace "Try Interchange Law"
-  try x = SomeComb (Try x)
+  try x = SimplComb
+    { combData = Try x
+    , combInline = False
+    , combRefs = combRefs x
+    }
+instance
+  ( CombApplicable repr
+  , CombAlternable repr
+  , CombLookable repr
+  , CombMatchable repr
+  , CombSelectable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombAlternable (KnotComb letName repr)
 
 -- CombApplicable
 data instance Comb CombApplicable repr a where
   Pure :: Production a -> Comb CombApplicable repr a
-  (:<*>:) :: SomeComb repr (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
-  (:<*:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr a
-  (:*>:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr b
+  (:<*>:) :: SimplComb repr (a -> b) -> SimplComb repr a -> Comb CombApplicable repr b
+  (:<*:) :: SimplComb repr a -> SimplComb repr b -> Comb CombApplicable repr a
+  (:*>:) :: SimplComb repr a -> SimplComb repr b -> Comb CombApplicable repr b
 infixl 4 :<*>:, :<*:, :*>:
-pattern (:<$>:) :: Production (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
+pattern (:<$>:) :: Production (a -> b) -> SimplComb repr a -> Comb CombApplicable repr b
 pattern t :<$>: x <- Comb (Pure t) :<*>: x
-pattern (:$>:) :: SomeComb repr a -> Production b -> Comb CombApplicable repr b
+pattern (:$>:) :: SimplComb repr a -> Production b -> Comb CombApplicable repr b
 pattern x :$>: t <- x :*>: Comb (Pure t)
-instance CombApplicable repr => Trans (Comb CombApplicable repr) repr where
-  trans = \case
-    Pure x -> pure (optimizeProduction x)
-    f :<*>: x -> trans f <*> trans x
-    x :<*: y -> trans x <* trans y
-    x :*>: y -> trans x *> trans y
+instance CombApplicable repr => Derivable (Comb CombApplicable repr) where
+  derive = \case
+    Pure x -> pure x
+    f :<*>: x -> derive f <*> derive x
+    x :<*: y -> derive x <* derive y
+    x :*>: y -> derive x *> derive y
 instance
   ( CombApplicable repr
   , CombAlternable repr
   , CombLookable repr
   , CombMatchable repr
   , CombSelectable repr
-  ) => CombApplicable (SomeComb repr) where
-  pure = SomeComb . Pure
+  ) => CombApplicable (SimplComb repr) where
+  pure a = SimplComb
+    { combData = Pure a
+    , combInline = False -- TODO: maybe True?
+    , combRefs = HS.empty
+    }
   f <$> Comb (Branch b l r) =
     branch b
-      ((H..) H..@ f <$> l)
-      ((H..) H..@ f <$> r)
+      ((Prod..) Prod..@ f <$> l)
+      ((Prod..) Prod..@ f <$> r)
     -- & trace "Branch Distributivity Law"
-  f <$> Comb (Conditional a ps bs d) =
-    conditional a ps
-      ((f <$>) Functor.<$> bs)
-      (f <$> d)
+  f <$> Comb (Conditional a bs def) =
+    conditional a
+      ((\(p, b) -> (p, f <$> b)) F.<$> bs)
+      (f <$> def)
     -- & trace "Conditional Distributivity Law"
   -- Being careful here to use (<*>),
-  -- instead of SomeComb (f <$> unOptComb x),
+  -- instead of SimplComb { combData = f <$> combData x },
   -- in order to apply the optimizations of (<*>).
   f <$> x = pure f <*> x
 
@@ -178,19 +306,19 @@ instance
     -- & trace "App Right Absorption Law"
   u <*> Comb Empty = u *> empty
     -- & trace "App Failure Weakening Law"
-  Comb (Pure f) <*> Comb (Pure x) = pure (f H..@ x)
+  Comb (Pure f) <*> Comb (Pure x) = pure (f Prod..@ x)
     -- & trace "Homomorphism Law"
   {-
   Comb (Pure f) <*> Comb (g :<$>: p) =
     -- This is basically a shortcut,
     -- it can be caught by one Composition Law
     -- and two Homomorphism Law.
-    (H..) H..@ f H..@ g <$> p
+    (Prod..) Prod..@ f Prod..@ g <$> p
     -- & trace "Functor Composition Law"
   -}
-  u <*> Comb (Pure x) = H.flip H..@ (H.$) H..@ x <$> u
+  u <*> Comb (Pure x) = Prod.flip Prod..@ (Prod.$) Prod..@ x <$> u
     -- & trace "Interchange Law"
-  u <*> Comb (v :<*>: w) = (((H..) <$> u) <*> v) <*> w
+  u <*> Comb (v :<*>: w) = (((Prod..) <$> u) <*> v) <*> w
     -- & trace "Composition Law"
   Comb (u :*>: v) <*> w = u *> (v <*> w)
     -- & trace "Reassociation Law 1"
@@ -199,9 +327,13 @@ instance
   u <*> Comb (v :$>: x) = (u <*> pure x) <* v
     -- & trace "Reassociation Law 3"
   p <*> Comb (NegLook q) =
-    (p <*> pure H.unit) <* negLook q
+    (p <*> pure Prod.unit) <* negLook q
     -- & trace "Absorption Law"
-  x <*> y = SomeComb (x :<*>: y)
+  x <*> y = SimplComb
+    { combData = x :<*>: y
+    , combInline = False
+    , combRefs = combRefs x <> combRefs y
+    }
 
   Comb Empty *> _ = empty
     -- & trace "App Right Absorption Law"
@@ -213,7 +345,11 @@ instance
     -- & trace "Identity Law"
   u *> Comb (v :*>: w) = (u *> v) *> w
     -- & trace "Associativity Law"
-  x *> y = SomeComb (x :*>: y)
+  x *> y = SimplComb
+    { combData = x :*>: y
+    , combInline = False
+    , combRefs = combRefs x <> combRefs y
+    }
 
   Comb Empty <* _ = empty
     -- & trace "App Right Absorption Law"
@@ -227,59 +363,55 @@ instance
     -- & trace "Identity Law"
   Comb (u :<*: v) <* w = u <* (v <* w)
     -- & trace "Associativity Law"
-  x <* y = SomeComb (x :<*: y)
+  x <* y = SimplComb
+    { combData = x :<*: y
+    , combInline = False
+    , combRefs = combRefs x <> combRefs y
+    }
+instance
+  ( CombApplicable repr
+  , CombAlternable repr
+  , CombLookable repr
+  , CombMatchable repr
+  , CombSelectable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombApplicable (KnotComb letName repr)
 
 -- CombFoldable
 data instance Comb CombFoldable repr a where
-  ChainPreC :: SomeComb repr (a -> a) -> SomeComb repr a -> Comb CombFoldable repr a
-  ChainPostC :: SomeComb repr a -> SomeComb repr (a -> a) -> Comb CombFoldable repr a
-instance CombFoldable repr => Trans (Comb CombFoldable repr) repr where
-  trans = \case
-    ChainPreC x y -> chainPre (trans x) (trans y)
-    ChainPostC x y -> chainPost (trans x) (trans y)
-instance CombFoldable repr => CombFoldable (SomeComb repr) where
-  chainPre x = SomeComb . ChainPreC x
-  chainPost x = SomeComb . ChainPostC x
-
--- Letable
-data instance Comb (Letable letName) repr a where
-  Shareable :: letName -> SomeComb repr a -> Comb (Letable letName) repr a
-  Ref :: Bool -> letName -> Comb (Letable letName) repr a
-instance
-  Letable letName repr =>
-  Trans (Comb (Letable letName) repr) repr where
-  trans = \case
-    Shareable n x -> shareable n (trans x)
-    Ref isRec n -> ref isRec n
+  ChainPre :: SimplComb repr (a -> a) -> SimplComb repr a -> Comb CombFoldable repr a
+  ChainPost :: SimplComb repr a -> SimplComb repr (a -> a) -> Comb CombFoldable repr a
+instance CombFoldable repr => Derivable (Comb CombFoldable repr) where
+  derive = \case
+    ChainPre op p -> chainPre (derive op) (derive p)
+    ChainPost p op -> chainPost (derive p) (derive op)
+instance CombFoldable repr => CombFoldable (SimplComb repr) where
+  chainPre op p = SimplComb
+    { combData = ChainPre op p
+    , combInline = False
+    , combRefs = combRefs op <> combRefs p
+    }
+  chainPost p op = SimplComb
+    { combData = ChainPost p op
+    , combInline = False
+    , combRefs = combRefs p <> combRefs op
+    }
 instance
-  (Letable letName repr, Typeable letName) =>
-  Letable letName (SomeComb repr) where
-  shareable n = SomeComb . Shareable n
-  ref isRec = SomeComb . Ref isRec
-
--- Letsable
-data instance Comb (Letsable letName) repr a where
-  Lets :: LetBindings letName (SomeComb repr) ->
-          SomeComb repr a -> Comb (Letsable letName) repr a
-instance
-  Letsable letName repr =>
-  Trans (Comb (Letsable letName) repr) repr where
-  trans = \case
-    Lets defs x -> lets ((\(SomeLet sub) -> SomeLet (trans sub)) Functor.<$> defs) (trans x)
-instance
-  (Letsable letName repr, Typeable letName) =>
-  Letsable letName (SomeComb repr) where
-  lets defs = SomeComb . Lets defs
+  ( CombFoldable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombFoldable (KnotComb letName repr)
 
 -- CombLookable
 data instance Comb CombLookable repr a where
-  Look :: SomeComb repr a -> Comb CombLookable repr a
-  NegLook :: SomeComb repr a -> Comb CombLookable repr ()
+  Look :: SimplComb repr a -> Comb CombLookable repr a
+  NegLook :: SimplComb repr a -> Comb CombLookable repr ()
   Eof :: Comb CombLookable repr ()
-instance CombLookable repr => Trans (Comb CombLookable repr) repr where
-  trans = \case
-    Look x -> look (trans x)
-    NegLook x -> negLook (trans x)
+instance CombLookable repr => Derivable (Comb CombLookable repr) where
+  derive = \case
+    Look x -> look (derive x)
+    NegLook x -> negLook (derive x)
     Eof -> eof
 instance
   ( CombAlternable repr
@@ -287,7 +419,7 @@ instance
   , CombLookable repr
   , CombSelectable repr
   , CombMatchable repr
-  ) => CombLookable (SomeComb repr) where
+  ) => CombLookable (SimplComb repr) where
   look p@(Comb Pure{}) = p
     -- & trace "Pure Look Law"
   look p@(Comb Empty) = p
@@ -300,13 +432,17 @@ instance
     -- & trace "Interchange Law"
   look (Comb (f :<$>: p)) = f <$> look p
     -- & trace "Interchange Law"
-  look x = SomeComb (Look x)
+  look x = SimplComb
+    { combData = Look x
+    , combInline = False
+    , combRefs = combRefs x
+    }
 
   negLook (Comb Pure{}) = empty
     -- & trace "Pure Negative-Look"
-  negLook (Comb Empty) = pure H.unit
+  negLook (Comb Empty) = pure Prod.unit
     -- & trace "Dead Negative-Look"
-  negLook (Comb (NegLook x)) = look (try x *> pure H.unit)
+  negLook (Comb (NegLook x)) = look (try x *> pure Prod.unit)
     -- & trace "Double Negation Law"
   negLook (Comb (Try x)) = negLook x
     -- & trace "Zero Consumption Law"
@@ -317,45 +453,86 @@ instance
     -- & trace "Transparency Law"
   negLook (Comb (p :$>: _)) = negLook p
     -- & trace "NegLook Idempotence Law"
-  negLook x = SomeComb (NegLook x)
+  negLook x = SimplComb
+    { combData = NegLook x
+    , combInline = False
+    , combRefs = combRefs x
+    }
 
-  eof = SomeComb Eof
+  eof = SimplComb
+    { combData = Eof
+    , combInline = True
+    , combRefs = HS.empty
+    }
+instance
+  ( CombLookable repr
+  , CombAlternable repr
+  , CombApplicable repr
+  , CombSelectable repr
+  , CombMatchable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombLookable (KnotComb letName repr)
 
 -- CombMatchable
 data instance Comb CombMatchable repr a where
-  Conditional :: Eq a =>
-    SomeComb repr a ->
-    [Production (a -> Bool)] ->
-    [SomeComb repr b] ->
-    SomeComb repr b ->
+  Conditional ::
+    SimplComb repr a ->
+    [(Production (a -> Bool), SimplComb repr b)] ->
+    SimplComb repr b ->
     Comb CombMatchable repr b
-instance CombMatchable repr => Trans (Comb CombMatchable repr) repr where
-  trans = \case
-    Conditional a ps bs b ->
-      conditional (trans a)
-        (optimizeProduction Functor.<$> ps)
-        (trans Functor.<$> bs) (trans b)
+instance CombMatchable repr => Derivable (Comb CombMatchable repr) where
+  derive = \case
+    Conditional a bs def ->
+      conditional (derive a)
+        ((\(p, b) -> (p, derive b)) F.<$> bs)
+        (derive def)
 instance
   ( CombApplicable repr
   , CombAlternable repr
   , CombLookable repr
   , CombSelectable repr
   , CombMatchable repr
-  ) => CombMatchable (SomeComb repr) where
-  conditional (Comb Empty) _ _ d = d
+  ) => CombMatchable (SimplComb repr) where
+  conditional (Comb Empty) _ def = def
     -- & trace "Conditional Absorption Law"
-  conditional p _ qs (Comb Empty)
-    | Foldable.all (\case { Comb Empty -> True; _ -> False }) qs = p *> empty
-      -- & trace "Conditional Weakening Law"
-  conditional a _ps bs (Comb Empty)
-    | Foldable.all (\case { Comb Empty -> True; _ -> False }) bs = a *> empty
+  conditional a bs (Comb Empty)
+    | Foldable.all (\case { (_, Comb Empty) -> True; _ -> False }) bs = a *> empty
       -- & trace "Conditional Weakening Law"
-  conditional (Comb (Pure a)) ps bs d =
-    Foldable.foldr (\(p, b) next ->
-      if runValue (p H..@ a) then b else next
-    ) d (List.zip ps bs)
+  conditional (Comb (Pure a)) bs def =
+    Foldable.foldr (\(p, b) acc ->
+      if runValue (p Prod..@ a) then b else acc
+    ) def bs
     -- & trace "Conditional Pure Law"
-  conditional a ps bs d = SomeComb (Conditional a ps bs d)
+  conditional a bs d = SimplComb
+    { combData = Conditional a bs d
+    , combInline = False
+    , combRefs = HS.unions
+        $ combRefs a
+        : combRefs d
+        : ((\(_p, b) -> combRefs b) F.<$> bs)
+    }
+instance
+  ( CombMatchable repr
+  , CombAlternable repr
+  , CombApplicable repr
+  , CombLookable repr
+  , CombSelectable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombMatchable (KnotComb letName repr) where
+  conditional a bs d = KnotComb
+    { knotCombOpens = HM.unions
+        $ knotCombOpens a
+        : knotCombOpens d
+        : ((\(_p, b) -> knotCombOpens b) F.<$> bs)
+    , knotCombOpen = \final -> TiedComb
+        { combSimpl = conditional
+            (combSimpl (knotCombOpen a final))
+            ((\(p, b) -> (p, combSimpl (knotCombOpen b final))) F.<$> bs)
+            (combSimpl (knotCombOpen d final))
+        }
+    }
 
 -- CombSatisfiable
 data instance Comb (CombSatisfiable tok) repr a where
@@ -373,75 +550,216 @@ data instance Comb (CombSatisfiable tok) repr a where
     Comb (CombSatisfiable tok) repr tok
 instance
   CombSatisfiable tok repr =>
-  Trans (Comb (CombSatisfiable tok) repr) repr where
-  trans = \case
-    SatisfyOrFail fs p -> satisfyOrFail fs (optimizeProduction p)
+  Derivable (Comb (CombSatisfiable tok) repr) where
+  derive = \case
+    SatisfyOrFail fs p -> satisfyOrFail fs p
 instance
   (CombSatisfiable tok repr, Typeable tok) =>
-  CombSatisfiable tok (SomeComb repr) where
-  satisfyOrFail fs = SomeComb . SatisfyOrFail fs
+  CombSatisfiable tok (SimplComb repr) where
+  satisfyOrFail fs p = SimplComb
+    { combData = SatisfyOrFail fs p
+    , combInline = False -- TODO: True? depending on p?
+    , combRefs = HS.empty
+    }
+instance
+  ( CombSatisfiable tok repr
+  , Typeable tok
+  , Eq letName
+  , Hashable letName
+  ) => CombSatisfiable tok (KnotComb letName repr)
 
 -- CombSelectable
 data instance Comb CombSelectable repr a where
   Branch ::
-    SomeComb repr (Either a b) ->
-    SomeComb repr (a -> c) ->
-    SomeComb repr (b -> c) ->
+    SimplComb repr (Either a b) ->
+    SimplComb repr (a -> c) ->
+    SimplComb repr (b -> c) ->
     Comb CombSelectable repr c
-instance CombSelectable repr => Trans (Comb CombSelectable repr) repr where
-  trans = \case
-    Branch lr l r -> branch (trans lr) (trans l) (trans r)
+instance CombSelectable repr => Derivable (Comb CombSelectable repr) where
+  derive = \case
+    Branch lr l r -> branch (derive lr) (derive l) (derive r)
 instance
   ( CombApplicable repr
   , CombAlternable repr
   , CombLookable repr
   , CombSelectable repr
   , CombMatchable repr
-  ) => CombSelectable (SomeComb repr) where
+  ) => CombSelectable (SimplComb repr) where
   branch (Comb Empty) _ _ = empty
     -- & trace "Branch Absorption Law"
   branch b (Comb Empty) (Comb Empty) = b *> empty
     -- & trace "Branch Weakening Law"
   branch (Comb (Pure lr)) l r =
     case runValue lr of
-      Left value -> l <*> pure Production{..}
+      Left value -> l <*> pure (Pair v c)
         where
-        prodValue = H.SomeData $ H.Var $ Identity value
-        prodCode = H.SomeData $ H.Var
+        v = Prod.SomeData $ Prod.Var $ Identity value
+        c = Prod.SomeData $ Prod.Var
           [|| case $$(runCode lr) of Left x -> x ||]
-      Right value -> r <*> pure Production{..}
+      Right value -> r <*> pure (Pair v c)
         where
-        prodValue = H.SomeData $ H.Var $ Identity value
-        prodCode = H.SomeData $ H.Var
+        v = Prod.SomeData $ Prod.Var $ Identity value
+        c = Prod.SomeData $ Prod.Var
           [|| case $$(runCode lr) of Right x -> x ||]
     -- & trace "Branch Pure Either Law"
   branch b (Comb (Pure l)) (Comb (Pure r)) =
-    Production{..} <$> b
+    Pair v c <$> b
     -- & trace "Branch Generalised Identity Law"
     where
-    prodValue = H.SomeData $ H.Var $ Identity $ either (runValue l) (runValue r)
-    prodCode = H.SomeData $ H.Var [|| either $$(runCode l) $$(runCode r) ||]
+    v = Prod.SomeData $ Prod.Var $ Identity $ either (runValue l) (runValue r)
+    c = Prod.SomeData $ Prod.Var [|| either $$(runCode l) $$(runCode r) ||]
   branch (Comb (x :*>: y)) p q = x *> branch y p q
     -- & trace "Interchange Law"
   branch b l (Comb Empty) =
-    branch (pure Production{..} <*> b) empty l
+    branch (pure (Pair v c) <*> b) empty l
     -- & trace "Negated Branch Law"
     where
-    prodValue = H.SomeData $ H.Var $ Identity $ either Right Left
-    prodCode = H.SomeData $ H.Var $ [||either Right Left||]
+    v = Prod.SomeData $ Prod.Var $ Identity $ either Right Left
+    c = Prod.SomeData $ Prod.Var $ [||either Right Left||]
   branch (Comb (Branch b (Comb Empty) (Comb (Pure lr)))) (Comb Empty) br =
-    branch (pure Production{..} <*> b) empty br
+    branch (pure (Pair v c) <*> b) empty br
     -- & trace "Branch Fusion Law"
     where
-    prodValue = H.SomeData $ H.Var $ Identity $ \case
+    v = Prod.SomeData $ Prod.Var $ Identity $ \case
       Left{} -> Left ()
       Right r ->
         case runValue lr r of
           Left{} -> Left ()
           Right rr -> Right rr
-    prodCode = H.SomeData $ H.Var
+    c = Prod.SomeData $ Prod.Var
       [|| \case Left{} -> Left ()
                 Right r -> case $$(runCode lr) r of
                              Left{} -> Left ()
                              Right rr -> Right rr ||]
-  branch b l r = SomeComb (Branch b l r)
+  branch b l r = SimplComb
+    { combData = Branch b l r
+    , combInline = False
+    , combRefs = HS.unions [ combRefs b, combRefs l, combRefs r ]
+    }
+instance
+  ( CombSelectable repr
+  , CombAlternable repr
+  , CombApplicable repr
+  , CombLookable repr
+  , CombMatchable repr
+  , Eq letName
+  , Hashable letName
+  ) => CombSelectable (KnotComb letName repr)
+
+-- CombRegisterableUnscoped
+data instance Comb CombRegisterableUnscoped repr a where
+  NewUnscoped :: UnscopedRegister a -> SimplComb repr a -> SimplComb repr b -> Comb CombRegisterableUnscoped repr b
+  GetUnscoped :: UnscopedRegister a -> Comb CombRegisterableUnscoped repr a
+  PutUnscoped :: UnscopedRegister a -> SimplComb repr a -> Comb CombRegisterableUnscoped repr ()
+instance CombRegisterableUnscoped repr => Derivable (Comb CombRegisterableUnscoped repr) where
+  derive = \case
+    NewUnscoped r ini x -> newUnscoped r (derive ini) (derive x)
+    GetUnscoped r -> getUnscoped r
+    PutUnscoped r x -> putUnscoped r (derive x)
+instance -- TODO: optimizations
+  ( CombRegisterableUnscoped repr
+  ) => CombRegisterableUnscoped (SimplComb repr) where
+  newUnscoped r ini x = SimplComb
+    { combData = NewUnscoped r ini x
+    , combInline = combInline ini && combInline x
+    , combRefs = combRefs ini <> combRefs x
+    }
+  getUnscoped r = SimplComb
+    { combData = GetUnscoped r
+    , combInline = True
+    , combRefs = HS.empty
+    }
+  putUnscoped r x = SimplComb
+    { combData = PutUnscoped r x
+    , combInline = combInline x
+    , combRefs = combRefs x
+    }
+instance
+  ( CombRegisterableUnscoped repr
+  , Eq letName
+  , Hashable letName
+  ) => CombRegisterableUnscoped (KnotComb letName repr) where
+
+-- Letsable
+data instance Comb (Letsable letName) repr a where
+  Lets ::
+    LetBindings letName (SimplComb repr) ->
+    SimplComb repr a ->
+    Comb (Letsable letName) repr a
+instance
+  Letsable letName repr =>
+  Derivable (Comb (Letsable letName) repr) where
+  derive = \case
+    Lets defs x -> lets
+      ((\(SomeLet sub) -> SomeLet (derive sub)) F.<$> defs)
+      (derive x)
+instance
+  (Letsable letName repr, Typeable letName) =>
+  Letsable letName (SimplComb repr) where
+  lets defs body = SimplComb
+    { combData = Lets defs body
+    , combInline = False
+    , combRefs = HS.unions
+        $ combRefs body
+        : ((\(SomeLet sub) -> combRefs sub) F.<$> HM.elems defs)
+    }
+instance
+  Letsable TH.Name repr =>
+  Letsable TH.Name (KnotComb TH.Name repr) where
+  lets defs body = KnotComb
+    { knotCombOpens =
+        HM.unions
+          $ knotCombOpens body
+          : ((\(SomeLet sub) -> SomeLet . knotCombOpen sub) F.<$> defs)
+          -- Not really necessary to include 'knotCombOpens' of 'defs' here
+          -- since there is only a single 'lets' at the top of the AST,
+          -- but well.
+          : ((\(SomeLet sub) -> knotCombOpens sub) F.<$> HM.elems defs)
+    , knotCombOpen = \final -> TiedComb
+        { combSimpl =
+          let bodySimpl = combSimpl $ knotCombOpen body final in
+          let defsSimpl = (\(SomeLet sub) -> SomeLet $ combSimpl $ knotCombOpen sub final) F.<$> defs in
+          let defsUsed = HS.unions
+                $ combRefs bodySimpl
+                : ((\(SomeLet sub) -> combRefs sub) F.<$> HM.elems defsSimpl) in
+          lets (HM.intersection defsSimpl (HS.toMap defsUsed)) bodySimpl
+        }
+    }
+
+-- Referenceable
+data instance Comb (Referenceable letName) repr a where
+  Ref :: Bool -> letName -> Comb (Referenceable letName) repr a
+instance
+  Referenceable letName repr =>
+  Derivable (Comb (Referenceable letName) repr) where
+  derive = \case
+    Ref isRec name -> ref isRec name
+instance
+  Referenceable TH.Name repr =>
+  Referenceable TH.Name (SimplComb repr) where
+  ref isRec name = SimplComb
+    { combData = Ref isRec name
+    , combInline = not isRec
+    , combRefs = HS.singleton name
+    }
+instance
+  Referenceable TH.Name repr =>
+  Referenceable TH.Name (KnotComb TH.Name repr) where
+  ref isRec name = KnotComb
+    { knotCombOpens = HM.empty
+    , knotCombOpen = \final ->
+      if isRec
+      then TiedComb
+        { combSimpl = ref isRec name
+        }
+      else case final HM.! name of
+        SomeLet a@TiedComb
+          { combSimpl = p@SimplComb{ combInline = True }
+          } -> a{combSimpl = unsafeSimplComb p}
+        SomeLet TiedComb
+          { combSimpl = SimplComb{ combRefs = refs }
+          } -> TiedComb
+            { combSimpl = (ref isRec name)
+              { combRefs = HS.insert name refs }
+            }
+    }