diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -96,3 +96,14 @@
 * Added `BlockCoins` instruction and `CutImmune` node.
 * Changed how cut compliance is determined, and stopped some incorrect factoring.
 * Removed unneeded flags for analysis.
+
+## 1.7.0.0 -- TBD
+
+* Added fields to the handlers to signify if they should generate a binding or not.
+* Added two `Inliner` modules to handle inlining in front- and back-ends.
+* Removed field from `Let` which contains the body, it was a wart.
+* Refactored the internal representation of static handlers, making them more uniform.
+* Added basic eta-reduction capabilities to the low-level generators: this can be improved and expanded!
+* Renamed `buildIterAlways` and `buildIterSame` to `bindIterAlways` and `bindIterSame`.
+* Renamed `StaHandler` to `AugmentedStaHandler`
+* 
diff --git a/parsley-core.cabal b/parsley-core.cabal
--- a/parsley-core.cabal
+++ b/parsley-core.cabal
@@ -5,7 +5,7 @@
 --                   | +------- breaking internal API changes
 --                   | | +----- non-breaking API additions
 --                   | | | +--- code changes with no API change
-version:             1.6.0.0
+version:             1.7.0.0
 synopsis:            A fast parser combinator library backed by Typed Template Haskell
 description:         This package contains the internals of the @parsley@ package.
                      .
@@ -66,6 +66,7 @@
                        Parsley.Internal.Frontend.Analysis.Cut,
                        Parsley.Internal.Frontend.Analysis.Dependencies,
                        Parsley.Internal.Frontend.Analysis.Flags,
+                       Parsley.Internal.Frontend.Analysis.Inliner,
 
                        Parsley.Internal.Backend,
                        Parsley.Internal.Backend.CodeGenerator,
@@ -73,6 +74,7 @@
 
                        Parsley.Internal.Backend.Analysis,
                        Parsley.Internal.Backend.Analysis.Coins,
+                       Parsley.Internal.Backend.Analysis.Inliner,
                        Parsley.Internal.Backend.Analysis.Relevancy,
 
                        Parsley.Internal.Backend.Machine,
@@ -85,6 +87,7 @@
                        Parsley.Internal.Backend.Machine.InputRep,
                        Parsley.Internal.Backend.Machine.Instructions,
                        Parsley.Internal.Backend.Machine.Ops,
+                       Parsley.Internal.Backend.Machine.THUtils,
 
                        Parsley.Internal.Backend.Machine.Types,
                        Parsley.Internal.Backend.Machine.Types.Coins,
diff --git a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/BindingOps.hs b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/BindingOps.hs
--- a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/BindingOps.hs
+++ b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/BindingOps.hs
@@ -12,7 +12,7 @@
 This module contains the parts of the code-base that deal with levity-polymorphic code.
 
 For performance, and to help GHC optimise, parsley takes an aggressive stance with unboxing
-and representing input using unlifted types. This means that the code generator is levity 
+and representing input using unlifted types. This means that the code generator is levity
 polymorphic. While the generated code itself is not polymorphic, to respect the soundness
 of GHC, any code that is generated which explicitly creates an unlifted value is kept in
 type-class methods and instantiated for every input type. All of these classes are found
@@ -76,7 +76,7 @@
 @since 1.4.0.0
 -}
 class JoinBuilder o where
-  {-| 
+  {-|
   Generate a let-bound join point and provide it to another continuation.
 
   @since 1.4.0.0
@@ -169,7 +169,7 @@
 #define deriveMarshalOps(_o)                                          \
 instance MarshalOps _o where                                          \
 {                                                                     \
-  dynHandler# sh = [||\ !(o# :: Rep _o) -> $$(sh [||o#||]) ||];       \
+  dynHandler# sh = [||\ (o# :: Rep _o) -> $$(sh [||o#||]) ||];        \
   dynCont# sk = [||\ x (o# :: Rep _o) -> $$(sk [||x||] [||o#||]) ||]; \
 };
 inputInstances(deriveMarshalOps)
diff --git a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Eval.hs b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Eval.hs
--- a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Eval.hs
+++ b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Eval.hs
@@ -149,10 +149,10 @@
 
 evalCatch :: (PositionOps (Rep o), HandlerOps o) => Machine s o xs (Succ n) r a -> Handler o (Machine s o) (o : xs) n r a -> MachineMonad s o xs n r a
 evalCatch (Machine k) h = freshUnique $ \u -> case h of
-  Always (Machine h) ->
-    liftM2 (\mk mh γ -> bindAlwaysHandler γ (buildHandler γ mh u) mk) k h
-  Same (Machine yes) (Machine no) ->
-    liftM3 (\mk myes mno γ -> bindSameHandler γ (buildYesHandler γ myes u) (buildHandler γ mno u) mk) k yes no
+  Always gh (Machine h) ->
+    liftM2 (\mk mh γ -> bindAlwaysHandler γ gh (buildHandler γ mh u) mk) k h
+  Same gyes (Machine yes) gno (Machine no) ->
+    liftM3 (\mk myes mno γ -> bindSameHandler γ gyes (buildYesHandler γ myes u) gno (buildHandler γ mno u) mk) k yes no
 
 evalTell :: Machine s o (o : xs) n r a -> MachineMonad s o xs n r a
 evalTell (Machine k) = k <&> \mk γ -> mk (γ {operands = Op (OFFSET (input γ)) (operands γ)})
@@ -182,10 +182,10 @@
   freshUnique $ \u1 ->   -- This one is used for the handler's offset from point of failure
     freshUnique $ \u2 -> -- This one is used for the handler's check and loop offset
       case h of
-        Always (Machine h) ->
-          liftM2 (\mh ctx γ -> buildIterAlways ctx μ l (buildHandler γ mh u1) (input γ) u2) h ask
-        Same (Machine yes) (Machine no) ->
-          liftM3 (\myes mno ctx γ ->  buildIterSame ctx μ l (buildYesHandler γ myes u1) (buildHandler γ mno u1) (input γ) u2) yes no ask
+        Always gh (Machine h) ->
+          liftM2 (\mh ctx γ -> bindIterAlways ctx μ l gh (buildHandler γ mh u1) (input γ) u2) h ask
+        Same gyes (Machine yes) gno (Machine no) ->
+          liftM3 (\myes mno ctx γ -> bindIterSame ctx μ l gyes (buildYesHandler γ myes u1) gno (buildHandler γ mno u1) (input γ) u2) yes no ask
 
 evalJoin :: ΦVar x -> MachineMonad s o (x : xs) n r a
 evalJoin φ = askΦ φ <&> resume
@@ -217,7 +217,7 @@
 evalLogEnter :: (?ops :: InputOps (Rep o), LogHandler o, HandlerOps o)
              => String -> Machine s o xs (Succ (Succ n)) r a -> MachineMonad s o xs (Succ n) r a
 evalLogEnter name (Machine mk) = freshUnique $ \u ->
-  liftM2 (\k ctx γ -> [|| Debug.Trace.trace $$(preludeString name '>' γ ctx "") $$(bindAlwaysHandler γ (logHandler name ctx γ u) k)||])
+  liftM2 (\k ctx γ -> [|| Debug.Trace.trace $$(preludeString name '>' γ ctx "") $$(bindAlwaysHandler γ True (logHandler name ctx γ u) k)||])
     (local debugUp mk)
     ask
 
diff --git a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Ops.hs b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Ops.hs
--- a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Ops.hs
+++ b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Ops.hs
@@ -45,8 +45,8 @@
     -- ** Join Point Operations
     setupJoinPoint,
     -- ** Iteration Operations
-    buildIterAlways,
-    buildIterSame,
+    bindIterAlways,
+    bindIterSame,
     -- ** Recursion Operations
     buildRec,
     -- ** Marshalling Operations
@@ -74,6 +74,7 @@
 import Parsley.Internal.Backend.Machine.InputRep       (Rep)
 import Parsley.Internal.Backend.Machine.Instructions   (Access(..))
 import Parsley.Internal.Backend.Machine.LetBindings    (Regs(..), Metadata(failureInputCharacteristic, successInputCharacteristic), InputCharacteristic(..))
+import Parsley.Internal.Backend.Machine.THUtils        (eta)
 import Parsley.Internal.Backend.Machine.Types          (MachineMonad, Machine(..), run)
 import Parsley.Internal.Backend.Machine.Types.Context
 import Parsley.Internal.Backend.Machine.Types.Dynamics (DynFunc, DynCont, DynHandler)
@@ -200,8 +201,8 @@
 
 @since 1.2.0.0
 -}
-fatal :: StaHandler s o a
-fatal = mkStaHandlerNoOffset (const [|| returnST Nothing ||])
+fatal :: AugmentedStaHandler s o a
+fatal = augmentHandlerSta Nothing (const [|| returnST Nothing ||])
 
 {-|
 Fails by evaluating the next handler with the current input. Makes
@@ -226,7 +227,7 @@
              -> (Γ s o (o : xs) n r a -> Code (ST s (Maybe a))) -- ^ Partial parser accepting the modified state.
              -> Word                                            -- ^ The unique identifier for the offset on failure.
              -> StaHandlerBuilder s o a
-buildHandler γ h u c = mkStaHandler c $ \o# -> h (γ {operands = Op (OFFSET c) (operands γ), input = mkOffset o# u})
+buildHandler γ h u c = fromStaHandler# $ \o# -> h (γ {operands = Op (OFFSET c) (operands γ), input = mkOffset o# u})
 
 {-|
 Converts a partially evaluated parser into a "yes" handler: this means that
@@ -239,7 +240,7 @@
                 -> (Γ s o xs n r a -> Code (ST s (Maybe a)))
                 -> Word
                 -> StaHandler s o a
-buildYesHandler γ h u = mkStaHandlerNoOffset $ \o# -> h (γ {input = mkOffset o# u})
+buildYesHandler γ h u = fromStaHandler# $ \o# -> h (γ {input = mkOffset o# u})
 
 -- Handler binding
 {-|
@@ -249,15 +250,14 @@
 
 @since 1.4.0.0
 -}
---TODO: annoyingly, a `try` on its own binds a handler, even though it's footprint is negligable
---      we should introduce a `noBinding` flag to the Always handler to mitigate this.
 bindAlwaysHandler :: forall s o xs n r a b. HandlerOps o
                   => Γ s o xs n r a                    -- ^ The state from which to capture the offset.
+                  -> Bool                              -- ^ Whether or not a binding is required
                   -> StaHandlerBuilder s o a           -- ^ The handler waiting to receive the captured offset and be bound.
                   -> (Γ s o xs (Succ n) r a -> Code b) -- ^ The parser to receive the binding.
                   -> Code b
-bindAlwaysHandler γ h k = bindHandler# @o (staHandler# (h (input γ))) $ \qh ->
-  k (γ {handlers = VCons (mkStaHandlerDyn (Just (input γ)) qh) (handlers γ)})
+bindAlwaysHandler γ needed h k = bindHandlerInline# needed (staHandler# (h (input γ))) $ \qh ->
+  k (γ {handlers = VCons (augmentHandler (Just (input γ)) qh) (handlers γ)})
 
 {-|
 Wraps around `bindHandler#` to create /three/ bindings for a handler that acts
@@ -269,18 +269,18 @@
 -}
 bindSameHandler :: forall s o xs n r a b. (HandlerOps o, PositionOps (Rep o))
                 => Γ s o xs n r a                    -- ^ The state from which to capture the offset.
+                -> Bool                              -- ^ Is a binding required for the matching handler?
                 -> StaHandler s o a                  -- ^ The handler that handles matching input.
+                -> Bool                              -- ^ Is a binding required for the mismatched handler?
                 -> StaHandlerBuilder s o a           -- ^ The handler that handles mismatched input.
                 -> (Γ s o xs (Succ n) r a -> Code b) -- ^ The parser to receive the composite handler.
                 -> Code b
-bindSameHandler γ yes no k = [||
-    let yesSame = $$(staHandler# yes (offset (input γ)))
-    in $$(bindHandler# @o (staHandler# (no (input γ))) $ \qno ->
-            let handler = mkStaHandler (input γ) $ \o ->
-                  [||if $$(same (offset (input γ)) o) then yesSame else $$qno $$o||]
-            in bindHandler# @o (staHandler# handler) $ \qhandler ->
-                k (γ {handlers = VCons (mkStaHandlerFull (input γ) qhandler [||yesSame||] qno) (handlers γ)}))
-  ||]
+bindSameHandler γ yesNeeded yes noNeeded no k =
+  bindYesInline# yesNeeded (staHandler# yes (offset (input γ))) $ \qyes ->
+    bindHandlerInline# noNeeded (staHandler# (no (input γ))) $ \qno ->
+      let handler o = [||if $$(same (offset (input γ)) o) then $$qyes else $$(staHandler# qno o)||]
+      in bindHandlerInline# @o True handler $ \qhandler ->
+          k (γ {handlers = VCons (augmentHandlerFull (input γ) qhandler qyes qno) (handlers γ)})
 
 {- Continuation Operations -}
 -- Basic continuations and operations
@@ -324,7 +324,7 @@
                      => StaSubroutine s o a x           -- ^ The subroutine @sub@ that will be called.
                      -> StaCont s o a x                 -- ^ The return continuation for the subroutine.
                      -> Code (Rep o)                    -- ^ The input to feed to @sub@.
-                     -> Vec (Succ n) (StaHandler s o a) -- ^ The stack from which to obtain the handler to pass to @sub@.
+                     -> Vec (Succ n) (AugmentedStaHandler s o a) -- ^ The stack from which to obtain the handler to pass to @sub@.
                      -> Code (ST s (Maybe a))
 callWithContinuation sub ret input (VCons h _) = staSubroutine# sub (dynCont ret) input (dynHandler h (failureInputCharacteristic (meta sub)))
 
@@ -391,45 +391,47 @@
 
 @since 1.4.0.0
 -}
-buildIterAlways :: forall s o a. RecBuilder o
-                => Ctx s o a                  -- ^ The context to keep the binding
-                -> MVar Void                  -- ^ The name of the binding.
-                -> Machine s o '[] One Void a -- ^ The body of the loop.
-                -> StaHandlerBuilder s o a    -- ^ What to do after the loop exits (by failing)
-                -> Offset o                   -- ^ The initial offset to provide to the loop
-                -> Word                       -- ^ The unique name for captured offset /and/ iteration offset
-                -> Code (ST s (Maybe a))
-buildIterAlways ctx μ l h o u =
-  bindIterHandler# @o (\qc# -> staHandler# (h (mkOffset qc# u))) $ \qhandler ->
+bindIterAlways :: forall s o a. RecBuilder o
+               => Ctx s o a                  -- ^ The context to keep the binding
+               -> MVar Void                  -- ^ The name of the binding.
+               -> Machine s o '[] One Void a -- ^ The body of the loop.
+               -> Bool                       -- ^ Does loop exit require a binding?
+               -> StaHandlerBuilder s o a    -- ^ What to do after the loop exits (by failing)
+               -> Offset o                   -- ^ The initial offset to provide to the loop
+               -> Word                       -- ^ The unique name for captured offset /and/ iteration offset
+               -> Code (ST s (Maybe a))
+bindIterAlways ctx μ l needed h o u =
+  bindIterHandlerInline# @o needed (\qc# -> staHandler# (h (mkOffset qc# u))) $ \qhandler ->
     bindIter# @o (offset o) $ \qloop qo# ->
       let off = mkOffset qo# u
-      in run l (Γ Empty noreturn off (VCons (mkStaHandlerDyn (Just off) [||$$qhandler $$(qo#)||]) VNil))
+      in run l (Γ Empty noreturn off (VCons (augmentHandler (Just off) (qhandler qo#)) VNil))
                (voidCoins (insertSub μ (mkStaSubroutine $ \_ o# _ -> [|| $$qloop $$(o#) ||]) ctx))
 
 {-|
-Similar to `buildIterAlways`, but builds a handler that performs in
+Similar to `bindIterAlways`, but builds a handler that performs in
 the same way as `bindSameHandler`.
 
 @since 1.4.0.0
 -}
-buildIterSame :: forall s o a. (RecBuilder o, HandlerOps o, PositionOps (Rep o))
-              => Ctx s o a                  -- ^ The context to store the binding in.
-              -> MVar Void                  -- ^ The name of the binding.
-              -> Machine s o '[] One Void a -- ^ The loop body.
-              -> StaHandler s o a           -- ^ The handler when input is the same.
-              -> StaHandlerBuilder s o a    -- ^ The handler when input differs.
-              -> Offset o                   -- ^ The initial offset of the loop.
-              -> Word                       -- ^ The unique name of the captured offsets /and/ the iteration offset.
-              -> Code (ST s (Maybe a))
-buildIterSame ctx μ l yes no o u =
-  bindHandler# @o (staHandler# yes) $ \qyes ->
-    bindIterHandler# @o (\qc# -> staHandler# (no (mkOffset qc# u))) $ \qno ->
-      let handler qc# = mkStaHandler (mkOffset @o qc# u) $ \o ->
-            [||if $$(same qc# o) then $$qyes $$(qc#) else $$qno $$(qc#) $$o||]
-      in bindIterHandler# @o (staHandler# . handler) $ \qhandler ->
+bindIterSame :: forall s o a. (RecBuilder o, HandlerOps o, PositionOps (Rep o))
+             => Ctx s o a                  -- ^ The context to store the binding in.
+             -> MVar Void                  -- ^ The name of the binding.
+             -> Machine s o '[] One Void a -- ^ The loop body.
+             -> Bool                       -- ^ Is a binding required for the matching handler?
+             -> StaHandler s o a           -- ^ The handler when input is the same.
+             -> Bool                       -- ^ Is a binding required for the differing handler?
+             -> StaHandlerBuilder s o a    -- ^ The handler when input differs.
+             -> Offset o                   -- ^ The initial offset of the loop.
+             -> Word                       -- ^ The unique name of the captured offsets /and/ the iteration offset.
+             -> Code (ST s (Maybe a))
+bindIterSame ctx μ l neededYes yes neededNo no o u =
+  bindHandlerInline# @o neededYes (staHandler# yes) $ \qyes ->
+    bindIterHandlerInline# neededNo (\qc# -> staHandler# (no (mkOffset qc# u))) $ \qno ->
+      let handler qc# o = [||if $$(same qc# o) then $$(staHandler# qyes qc#) else $$(staHandler# (qno qc#) o)||]
+      in bindIterHandlerInline# @o True handler $ \qhandler ->
         bindIter# @o (offset o) $ \qloop qo# ->
           let off = mkOffset qo# u
-          in run l (Γ Empty noreturn off (VCons (mkStaHandlerFull off [||$$qhandler $$(qo#)||] [||$$qyes $$(qo#)||] [||$$qno $$(qo#)||]) VNil))
+          in run l (Γ Empty noreturn off (VCons (augmentHandlerFull off (qhandler qo#) (staHandler# qyes qo#) (qno qo#)) VNil))
                    (voidCoins (insertSub μ (mkStaSubroutine $ \_ o# _ -> [|| $$qloop $$(o#) ||]) ctx))
 
 {- Recursion Operations -}
@@ -451,9 +453,30 @@
 buildRec μ rs ctx k meta =
   takeFreeRegisters rs ctx $ \ctx ->
     bindRec# @o $ \qself qret qo# qh ->
-      run k (Γ Empty (mkStaContDyn qret) (mkOffset qo# 0) (VCons (mkStaHandlerDyn Nothing qh) VNil))
+      run k (Γ Empty (mkStaContDyn qret) (mkOffset qo# 0) (VCons (augmentHandlerDyn Nothing qh) VNil))
             (insertSub μ (mkStaSubroutineMeta meta $ \k o# h -> [|| $$qself $$k $$(o#) $$h ||]) (nextUnique ctx))
 
+{- Binding Operations -}
+bindHandlerInline# :: forall o s a b. HandlerOps o
+                   => Bool
+                   -> StaHandler# s o a
+                   -> (StaHandler s o a -> Code b)
+                   -> Code b
+bindHandlerInline# True  h k = bindHandler# @o h (k . fromDynHandler)
+bindHandlerInline# False h k = k (fromStaHandler# h)
+
+bindYesInline# :: Bool -> Code a -> (Code a -> Code b) -> Code b
+bindYesInline# True  v k = [|| let yesSame = $$v in $$(k [||yesSame||]) ||]
+bindYesInline# False v k = k v
+
+bindIterHandlerInline# :: forall o s a b. RecBuilder o
+                       => Bool
+                       -> (Code (Rep o) -> StaHandler# s o a)
+                       -> ((Code (Rep o) -> StaHandler s o a) -> Code b)
+                       -> Code b
+bindIterHandlerInline# True  h k = bindIterHandler# @o h $ \qh -> k (\qo -> fromDynHandler [||$$qh $$qo||])
+bindIterHandlerInline# False h k = k (fromStaHandler# . h)
+
 {- Marshalling Operations -}
 {-|
 Wraps around `dynHandler#`, but ensures that if the `StaHandler`
@@ -464,9 +487,8 @@
 
 @since 1.5.0.0
 -}
-dynHandler :: forall s o a. MarshalOps o => StaHandler s o a -> InputCharacteristic -> DynHandler s o a
-dynHandler (StaHandler _ sh Nothing)  = dynHandler# @o . staHandlerCharacteristicSta sh
-dynHandler (StaHandler _ _ (Just dh)) = staHandlerCharacteristicDyn dh (dynHandler# @o . const)
+dynHandler :: forall s o a. MarshalOps o => AugmentedStaHandler s o a -> InputCharacteristic -> DynHandler s o a
+dynHandler h = staHandlerCharacteristicDyn h (eta . dynHandler# @o)
 
 {-|
 Wraps around `dynCont#`, but ensures that if the `StaCont`
@@ -475,7 +497,7 @@
 @since 1.4.0.0
 -}
 dynCont :: forall s o a x. MarshalOps o => StaCont s o a x -> DynCont s o a x
-dynCont (StaCont sk Nothing) = dynCont# @o sk
+dynCont (StaCont sk Nothing)  = dynCont# @o sk
 dynCont (StaCont _ (Just dk)) = dk
 
 {- Log Operations =-}
@@ -486,8 +508,8 @@
 @since 1.2.0.0
 -}
 logHandler :: (?ops :: InputOps (Rep o), LogHandler o) => String -> Ctx s o a -> Γ s o xs (Succ n) ks a -> Word -> StaHandlerBuilder s o a
-logHandler name ctx γ u o = let VCons h _ = handlers γ in mkStaHandler o $ \o# -> [||
-    trace $$(preludeString name '<' (γ {input = mkOffset o# u}) ctx (color Red " Fail")) $$(staHandler# h o#)
+logHandler name ctx γ u _ = let VCons h _ = handlers γ in fromStaHandler# $ \o# -> let o = mkOffset o# u in [||
+    trace $$(preludeString name '<' (γ {input = o}) ctx (color Red " Fail")) $$(staHandlerEval h o)
   ||]
 
 {-|
diff --git a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/State.hs b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/State.hs
--- a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/State.hs
+++ b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/State.hs
@@ -18,7 +18,7 @@
 
 import Parsley.Internal.Backend.Machine.Defunc        (Defunc)
 import Parsley.Internal.Backend.Machine.Types.Offset  (Offset)
-import Parsley.Internal.Backend.Machine.Types.Statics (StaCont, StaHandler)
+import Parsley.Internal.Backend.Machine.Types.Statics (StaCont, AugmentedStaHandler)
 import Parsley.Internal.Common.Vec                    (Vec)
 
 {-|
@@ -41,8 +41,8 @@
 
 @since 1.4.0.0
 -}
-data Γ s o xs n r a = Γ { operands :: OpStack xs               -- ^ The current values available for applicative application.
-                        , retCont  :: StaCont s o a r          -- ^ The current return continuation when this parser is finished.
-                        , input    :: Offset o                 -- ^ The current offset into the input of the parser.
-                        , handlers :: Vec n (StaHandler s o a) -- ^ The failure handlers that are used to process failure during a parser.
+data Γ s o xs n r a = Γ { operands :: OpStack xs                        -- ^ The current values available for applicative application.
+                        , retCont  :: StaCont s o a r                   -- ^ The current return continuation when this parser is finished.
+                        , input    :: Offset o                          -- ^ The current offset into the input of the parser.
+                        , handlers :: Vec n (AugmentedStaHandler s o a) -- ^ The failure handlers that are used to process failure during a parser.
                         }
diff --git a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/Statics.hs b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/Statics.hs
--- a/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/Statics.hs
+++ b/src/ghc-8.10+/Parsley/Internal/Backend/Machine/Types/Statics.hs
@@ -17,16 +17,18 @@
 -}
 module Parsley.Internal.Backend.Machine.Types.Statics (
     -- * Handlers
-    StaHandler#, StaHandler(..), StaHandlerCase, WStaHandler#, WDynHandler,
+    StaHandler#, StaHandler, AugmentedStaHandler, StaHandlerCase,
 
-    -- ** @StaHandler@ Builders
-    -- | The following functions are builders of `StaHandler`.
-    mkStaHandler, mkStaHandlerNoOffset, mkStaHandlerDyn, mkStaHandlerFull,
+    -- ** @StaHandler@ Operations
+    fromStaHandler#, fromDynHandler, staHandler#,
 
-    -- ** @StaHandler@ Interpreters
+    -- ** @AugmentedStaHandler@ Builders
+    -- | The following functions are builders of `AugmentedStaHandler`.
+    augmentHandler, augmentHandlerSta, augmentHandlerDyn, augmentHandlerFull,
+
+    -- ** @AugmentedStaHandler@ Interpreters
     -- | The following functions interpret or extract information from `StaHandler`.
-    staHandler#, staHandlerEval,
-    staHandlerCharacteristicSta, staHandlerCharacteristicDyn,
+    staHandlerEval, staHandlerCharacteristicSta, staHandlerCharacteristicDyn,
 
     -- * Return Continuations
     StaCont#, StaCont(..),
@@ -66,83 +68,100 @@
 mkStaHandler# dh qo# = [||$$dh $$(qo#)||]
 
 {-|
-Compared with `StaHandler#`, this type allows for the encoding of various static
-properties of handlers which can be carried around during the lifetime of the handlers.
-This information allows the engine to optimise more aggressively, leveraging
-domain-specific optimisation data.
+Encapsulates a static handler with its possible dynamic origin for costless conversion.
 
-@since 1.5.0.0
+@since 1.7.0.0
 -}
-data StaHandler s o a =
-  StaHandler
-    (Maybe (Offset o))                         -- ^ The statically bound offset for this handler, if available.
-    (StaHandlerCase WStaHandler# s o a)        -- ^ The static function representing this handler when offsets are incomparable.
-    (Maybe (StaHandlerCase WDynHandler s o a)) -- ^ The dynamic handler that has been wrapped in this handler, if available.
+data StaHandler s o a = StaHandler {
+    {-|
+    Extracts the raw static component out of a static handler.
 
+    @since 1.7.0.0
+    -}
+    staHandler# :: StaHandler# s o a,
+    dynOrigin :: Maybe (DynHandler s o a)
+  }
+
+dynHandler :: (StaHandler# s o a -> DynHandler s o a) -> StaHandler s o a -> DynHandler s o a
+dynHandler conv = fromMaybe . conv . staHandler# <*> dynOrigin
+
 {-|
-Given a static handler, extracts the underlying handler which
-has "forgotten" any static domain-specific information it had been
-attached to.
+Builds a `StaHandler` out of a `StaHandler#`, assumed to have no dynamic component.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-staHandler# :: StaHandler s o a -> StaHandler# s o a
-staHandler# (StaHandler _ sh _) = unWrapSta (unknown sh)
+fromStaHandler# :: StaHandler# s o a -> StaHandler s o a
+fromStaHandler# h = StaHandler h Nothing
 
-_mkStaHandler :: Maybe (Offset o) -> StaHandler# s o a -> StaHandler s o a
-_mkStaHandler o sh = StaHandler o (mkUnknownSta sh) Nothing
+{-|
+Builds a `StaHandler` out of a `DynHandler`, which is converted in the process.
 
+@since 1.7.0.0
+-}
+fromDynHandler :: forall s o a. DynHandler s o a -> StaHandler s o a
+fromDynHandler h = StaHandler (mkStaHandler# @o h) (Just h)
+
 {-|
-Augments a `StaHandler#` with information about what the offset is that
-the handler has captured. This is a purely static handler, which is not
-derived from a dynamic one.
+Compared with `StaHandler#`, this type allows for the encoding of various static
+properties of handlers which can be carried around during the lifetime of the handlers.
+This information allows the engine to optimise more aggressively, leveraging
+domain-specific optimisation data.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-mkStaHandler :: Offset o -> StaHandler# s o a -> StaHandler s o a
-mkStaHandler = _mkStaHandler . Just
+data AugmentedStaHandler s o a =
+  AugmentedStaHandler
+    (Maybe (Offset o))     -- ^ The statically bound offset for this handler, if available.
+    (StaHandlerCase s o a) -- ^ The relevant cases for the handlers behaviour
 
 {-|
-Converts a `StaHandler#` into a `StaHandler` without any information
-about the captured offset. This is a purely static handler, not derived
-from a dynamic one.
+Augments a `StaHandler#` with information about what the offset is that
+the handler has captured. This is a purely static handler, which is not
+derived from a dynamic one.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-mkStaHandlerNoOffset :: StaHandler# s o a -> StaHandler s o a
-mkStaHandlerNoOffset = _mkStaHandler Nothing
+augmentHandlerSta :: Maybe (Offset o) -> StaHandler# s o a -> AugmentedStaHandler s o a
+augmentHandlerSta o = augmentHandler o . fromStaHandler#
 
 {-|
 Converts a `Parsley.Internal.Machine.Types.Dynamics.DynHandler` into a
-`StaHandler` taking into account the possibility that captured offset
+`AugmentedStaHandler` taking into account the possibility that captured offset
 information is available. The dynamic handler used to construct this
 static handler is maintained as the origin of the handler. This means
 if it is converted back the conversion is free.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-mkStaHandlerDyn :: forall s o a. Maybe (Offset o) -> DynHandler s o a -> StaHandler s o a
-mkStaHandlerDyn c dh = StaHandler c (mkUnknownSta (mkStaHandler# @o dh)) (Just (mkUnknownDyn dh))
+augmentHandlerDyn :: forall s o a. Maybe (Offset o) -> DynHandler s o a -> AugmentedStaHandler s o a
+augmentHandlerDyn c = augmentHandler c . fromDynHandler
 
 {-|
+Augments a static handler with information about its captured offset.
+
+@since 1.7.0.0
+-}
+augmentHandler :: Maybe (Offset o) -> StaHandler s o a -> AugmentedStaHandler s o a
+augmentHandler c = AugmentedStaHandler c . mkUnknown
+
+{-|
 When the behaviours of a handler given input that matches or does not match
 its captured offset are known, this function can be used to construct a
-`StaHandler` that stores this information. This can in turn be used in
+`AugmentedStaHandler` that stores this information. This can in turn be used in
 conjunction with `staHandlerEval` to statically refine the application of
 a handler to its argument.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-mkStaHandlerFull :: forall s o a. Offset o -- ^ The offset captured by the creation of the handler.
-                 -> DynHandler s o a       -- ^ The full handler, which can be used when offsets are incomparable and must perform the check.
-                 -> Code (ST s (Maybe a))  -- ^ The code that is executed when the captured offset matches the input.
-                 -> DynHandler s o a       -- ^ The handler to be executed when offsets are known not to match.
-                 -> StaHandler s o a       -- ^ A handler that carries this information around for later refinement.
-mkStaHandlerFull c handler yes no = StaHandler (Just c)
-  (mkFullSta (mkStaHandler# @o handler)
-             yes
-             (mkStaHandler# @o no))
-  (Just (mkFullDyn handler yes no))
+augmentHandlerFull :: Offset o                  -- ^ The offset captured by the creation of the handler.
+                   -> StaHandler s o a          -- ^ The full handler, which can be used when offsets are incomparable and must perform the check.
+                   -> Code (ST s (Maybe a))     -- ^ The code that is executed when the captured offset matches the input.
+                   -> StaHandler s o a          -- ^ The handler to be executed when offsets are known not to match.
+                   -> AugmentedStaHandler s o a -- ^ A handler that carries this information around for later refinement.
+augmentHandlerFull c handler yes no = AugmentedStaHandler (Just c)
+  (mkFull handler
+          yes
+          no)
 
 {-|
 Unlike `staHandler#`, which returns a handler that accepts @'Code' ('Rep' o)@, this
@@ -151,93 +170,71 @@
 
   * If the handler has a registered captured offset, and these offsets are comparable:
 
-      * If the offsets are equal, use the code to be executed on matching offset (See `mkStaHandlerFull`)
-      * If the offsets are not equal, invoke the sub-handler, skipping the if check (see `mkStaHandlerFull`)
+      * If the offsets are equal, use the code to be executed on matching offset (See `augmentHandlerFull`)
+      * If the offsets are not equal, invoke the sub-handler, skipping the if check (see `augmentHandlerFull`)
 
   * If the handler is missing a captured offset, or they are incomparable (from different sources)
      then execute the full handler, which will perform a runtime check for equivalence.
 
-@since 1.4.0.0
+@since 1.7.0.0
 -}
-staHandlerEval :: StaHandler s o a -> Offset o -> Code (ST s (Maybe a))
-staHandlerEval (StaHandler (Just c) sh _) o
-  | Just True <- same c o            = maybe (unWrapSta (unknown sh)) const (yesSame sh) (offset o)
-  | Just False <- same c o           = unWrapSta (fromMaybe (unknown sh) (notSame sh)) (offset o)
-staHandlerEval (StaHandler _ sh _) o = unWrapSta (unknown sh) (offset o)
-
-staHandlerCharacteristic :: StaHandlerCase h s o a -> (Code (ST s (Maybe a)) -> h s o a) -> InputCharacteristic -> h s o a
-staHandlerCharacteristic sh conv NeverConsumes      = maybe (unknown sh) conv (yesSame sh)
-staHandlerCharacteristic sh _    (AlwaysConsumes _) = fromMaybe (unknown sh) (notSame sh)
-staHandlerCharacteristic sh _    MayConsume         = unknown sh
+staHandlerEval :: AugmentedStaHandler s o a -> Offset o -> Code (ST s (Maybe a))
+staHandlerEval (AugmentedStaHandler (Just c) sh) o
+  | Just True <- same c o                   = maybe (staHandler# (unknown sh)) const (yesSame sh) (offset o)
+  | Just False <- same c o                  = staHandler# (fromMaybe (unknown sh) (notSame sh)) (offset o)
+staHandlerEval (AugmentedStaHandler _ sh) o = staHandler# (unknown sh) (offset o)
 
 {-|
-Selects the correct case out of a `StaHandlerCase` depending on what the `InputCharacteristic` that
+Selects the correct case out of a `AugmentedStaHandler` depending on what the `InputCharacteristic` that
 governs the use of the handler is. This means that it can select any of the three cases.
 
-@since 1.5.0.0
+@since 1.7.0.0
 -}
-staHandlerCharacteristicSta :: StaHandlerCase WStaHandler# s o a -> InputCharacteristic -> StaHandler# s o a
-staHandlerCharacteristicSta h = unWrapSta . staHandlerCharacteristic h (WrapSta . const)
+staHandlerCharacteristic :: AugmentedStaHandler s o a -> (StaHandler# s o a -> DynHandler s o a) -> InputCharacteristic -> StaHandler s o a
+staHandlerCharacteristic (AugmentedStaHandler _ sh) conv NeverConsumes      = maybe (unknown sh) (StaHandler <$> const <*> Just . conv . const) (yesSame sh)
+staHandlerCharacteristic (AugmentedStaHandler _ sh) _    (AlwaysConsumes _) = fromMaybe (unknown sh) (notSame sh)
+staHandlerCharacteristic (AugmentedStaHandler _ sh) _    MayConsume         = unknown sh
 
 {-|
-Selects the correct case out of a `StaHandlerCase` depending on what the `InputCharacteristic` that
-governs the use of the handler is. This means that it can select any of the three cases.
+Selects the correct case out of a `AugmentedStaHandler` depending on what the `InputCharacteristic` that
+governs the use of the handler is. This means that it can select any of the three cases. Extracts the
+static handler out of the result.
 
-@since 1.5.0.0
+@since 1.7.0.0
 -}
-staHandlerCharacteristicDyn :: StaHandlerCase WDynHandler s o a
-                            -> (Code (ST s (Maybe a)) -> DynHandler s o a) -- ^ How to convert the input-same case to a `DynHandler`.
-                            -> InputCharacteristic
-                            -> DynHandler s o a
-staHandlerCharacteristicDyn h conv = unWrapDyn . staHandlerCharacteristic h (WrapDyn . conv)
+staHandlerCharacteristicSta :: AugmentedStaHandler s o a -> InputCharacteristic -> StaHandler# s o a
+staHandlerCharacteristicSta sh = staHandler# . staHandlerCharacteristic sh undefined
 
 {-|
+Selects the correct case out of a `AugmentedStaHandler` depending on what the `InputCharacteristic` that
+governs the use of the handler is. This means that it can select any of the three cases. Extracts a
+dynamic result out of the static handler given a conversion function.
+
+@since 1.7.0.0
+-}
+staHandlerCharacteristicDyn :: AugmentedStaHandler s o a -> (StaHandler# s o a -> DynHandler s o a) -> InputCharacteristic -> DynHandler s o a
+staHandlerCharacteristicDyn sh conv = dynHandler conv . staHandlerCharacteristic sh conv
+
+{-|
 Represents potentially three handlers: one for unknown offset cases, one for offset known to be
-the same, and another for offset known to be different (see `mkStaHandlerFull`). Parameterised by
-a generic handler type, which is instantiated to one of `WStaHandler#` or `WDynHandler`.
+the same, and another for offset known to be different (see `augmentHandlerFull`).
 
-@since 1.5.0.0
+@since 1.7.0.0
 -}
-data StaHandlerCase h s (o :: Type) a = StaHandlerCase {
+data StaHandlerCase s (o :: Type) a = StaHandlerCase {
   -- | The static function representing this handler when offsets are incomparable.
-  unknown :: h s o a,
+  unknown :: StaHandler s o a,
   -- | The static value representing this handler when offsets are known to match, if available.
   yesSame :: Maybe (Code (ST s (Maybe a))),
   -- | The static function representing this handler when offsets are known not to match, if available.
-  notSame :: Maybe (h s o a)
+  notSame :: Maybe (StaHandler s o a)
 }
 
-{-|
-Wraps a `StaHandler#`.
-
-@since 1.5.0.0
--}
-newtype WStaHandler# s o a = WrapSta { unWrapSta :: StaHandler# s o a }
-
-{-|
-Wraps a `DynHandler`.
-
-@since 1.5.0.0
--}
-newtype WDynHandler s o a = WrapDyn { unWrapDyn :: DynHandler s o a }
-
-mkUnknown :: h s o a -> StaHandlerCase h s o a
+mkUnknown :: StaHandler s o a -> StaHandlerCase s o a
 mkUnknown h = StaHandlerCase h Nothing Nothing
 
-mkUnknownSta :: StaHandler# s o a -> StaHandlerCase WStaHandler# s o a
-mkUnknownSta = mkUnknown . WrapSta
-
-mkUnknownDyn :: DynHandler s o a -> StaHandlerCase WDynHandler s o a
-mkUnknownDyn = mkUnknown . WrapDyn
-
-mkFull :: h s o a -> Code (ST s (Maybe a)) -> h s o a -> StaHandlerCase h s o a
+mkFull :: StaHandler s o a -> Code (ST s (Maybe a)) -> StaHandler s o a -> StaHandlerCase s o a
 mkFull h yes no = StaHandlerCase h (Just yes) (Just no)
-
-mkFullSta :: StaHandler# s o a -> Code (ST s (Maybe a)) -> StaHandler# s o a -> StaHandlerCase WStaHandler# s o a
-mkFullSta h yes no = mkFull (WrapSta h) yes (WrapSta no)
-
-mkFullDyn :: DynHandler s o a -> Code (ST s (Maybe a)) -> DynHandler s o a -> StaHandlerCase WDynHandler s o a
-mkFullDyn h yes no = mkFull (WrapDyn h) yes (WrapDyn no)
 
 -- Continuations
 {-|
diff --git a/src/ghc-8.6+/Parsley/Internal/Backend/Machine/Eval.hs b/src/ghc-8.6+/Parsley/Internal/Backend/Machine/Eval.hs
--- a/src/ghc-8.6+/Parsley/Internal/Backend/Machine/Eval.hs
+++ b/src/ghc-8.6+/Parsley/Internal/Backend/Machine/Eval.hs
@@ -112,8 +112,8 @@
 evalCatch (Machine k) (Machine h) = liftM2 (\mk mh γ -> setupHandler γ (buildHandler γ mh) mk) k h
 
 evalHandler :: PositionOps o => Instructions.Handler o (Machine s o) (o : xs) n r a -> Machine s o (o : xs) n r a
-evalHandler (Instructions.Always k) = k
-evalHandler (Instructions.Same yes no) =
+evalHandler (Instructions.Always _ k) = k
+evalHandler (Instructions.Same _ yes _ no) =
   Machine (evalDup (
     Machine (evalTell (
       Machine (evalLift2 SAME (
diff --git a/src/ghc/Parsley/Internal/Backend/Analysis.hs b/src/ghc/Parsley/Internal/Backend/Analysis.hs
--- a/src/ghc/Parsley/Internal/Backend/Analysis.hs
+++ b/src/ghc/Parsley/Internal/Backend/Analysis.hs
@@ -12,10 +12,12 @@
 -}
 module Parsley.Internal.Backend.Analysis (
     coinsNeeded,
+    shouldInline,
     relevancy
   ) where
 
 import Parsley.Internal.Backend.Analysis.Coins (coinsNeeded)
+import Parsley.Internal.Backend.Analysis.Inliner (shouldInline)
 import Parsley.Internal.Backend.Analysis.Relevancy (relevancy)
 
 {- TODO
diff --git a/src/ghc/Parsley/Internal/Backend/Analysis/Coins.hs b/src/ghc/Parsley/Internal/Backend/Analysis/Coins.hs
--- a/src/ghc/Parsley/Internal/Backend/Analysis/Coins.hs
+++ b/src/ghc/Parsley/Internal/Backend/Analysis/Coins.hs
@@ -78,5 +78,5 @@
 alg (MetaInstr BlockCoins (Const4 k))       = first (const zero) k
 
 algHandler :: Handler o (Const4 (Coins, Bool)) xs n r a -> (Coins, Bool)
-algHandler (Same yes no) = algCatch (getConst4 yes) (getConst4 no)
-algHandler (Always k) = getConst4 k
+algHandler (Same _ yes _ no) = algCatch (getConst4 yes) (getConst4 no)
+algHandler (Always _ k) = getConst4 k
diff --git a/src/ghc/Parsley/Internal/Backend/Analysis/Inliner.hs b/src/ghc/Parsley/Internal/Backend/Analysis/Inliner.hs
new file mode 100644
--- /dev/null
+++ b/src/ghc/Parsley/Internal/Backend/Analysis/Inliner.hs
@@ -0,0 +1,69 @@
+{-|
+Module      : Parsley.Internal.Backend.Analysis.Inliner
+Description : Determines whether a machine should be inlined.
+License     : BSD-3-Clause
+Maintainer  : Jamie Willis
+Stability   : experimental
+
+Exposes an inliner analysis that determines whether or not a given machine should be inlined
+as opposed to bound in the generated code.
+
+@since 1.7.0.0
+-}
+module Parsley.Internal.Backend.Analysis.Inliner (shouldInline) where
+
+import Data.Ratio                       ((%))
+import Parsley.Internal.Backend.Machine (Instr(..), Handler(..), Access(Hard, Soft))
+import Parsley.Internal.Common.Indexed  (cata4, Fix4, Nat)
+
+inlineThreshold :: Rational
+inlineThreshold = 13 % 10
+
+{-|
+Provides a conservative estimate on whether or not each of the elements of the stack on
+entry to a machine are actually used in the computation.
+
+@since 1.7.0.0
+-}
+shouldInline :: Fix4 (Instr o) xs n r a -> Bool
+shouldInline = (< inlineThreshold) . getWeight . cata4 (InlineWeight . alg)
+
+newtype InlineWeight xs (n :: Nat) r a = InlineWeight { getWeight :: Rational }
+
+alg :: Instr o InlineWeight xs n r a -> Rational
+alg Ret                = 0
+alg (Push _ k)         = 0 + getWeight k
+alg (Pop k)            = 0 + getWeight k
+alg (Lift2 _ k)        = 1 % 5 + getWeight k
+alg (Sat _ k)          = 1 + getWeight k
+alg (Call _ k)         = 2 % 3 + getWeight k
+alg (Jump _)           = 0
+alg Empt               = 0
+alg (Commit k)         = 0 + getWeight k
+alg (Catch k h)        = (if handlerInlined h then 0 else 1 % 4) + getWeight k + algHandler h
+alg (Tell k)           = 0 + getWeight k
+alg (Seek k)           = 0 + getWeight k
+alg (Case p q)         = 1 % 3 + getWeight p + getWeight q
+alg (Choices _ ks def) = fromIntegral (length ks + 1) % 3 + sum (map getWeight ks) + getWeight def
+alg (Iter _ b h)       = 2 % 3 + getWeight b + algHandler h
+alg (Join _)           = 0
+alg (MkJoin _ b k)     = 2 % 5 + getWeight b + getWeight k
+alg (Swap k)           = 0 + getWeight k
+alg (Dup k)            = 1 % 10 + getWeight k
+alg (Make _ Hard k)    = 1 % 3 + getWeight k
+alg (Get _ Hard k)     = 1 % 3 + getWeight k
+alg (Put _ Hard k)     = 1 % 3 + getWeight k
+alg (Make _ Soft k)    = 1 % 10 + getWeight k
+alg (Get _ Soft k)     = 0 + getWeight k
+alg (Put _ Soft k)     = 1 % 10 + getWeight k
+alg (LogEnter _ k)     = 1 % 4 + getWeight k
+alg (LogExit _ k)      = 1 % 4 + getWeight k
+alg (MetaInstr _ k)    = 0 + getWeight k
+
+algHandler :: Handler o InlineWeight xs n r a -> Rational
+algHandler (Always _ h) = getWeight h
+algHandler (Same _ y _ n) = getWeight y + getWeight n
+
+handlerInlined :: Handler o k xs n r a -> Bool
+handlerInlined (Always True _) = True
+handlerInlined _               = False
diff --git a/src/ghc/Parsley/Internal/Backend/Analysis/Relevancy.hs b/src/ghc/Parsley/Internal/Backend/Analysis/Relevancy.hs
--- a/src/ghc/Parsley/Internal/Backend/Analysis/Relevancy.hs
+++ b/src/ghc/Parsley/Internal/Backend/Analysis/Relevancy.hs
@@ -73,5 +73,5 @@
 alg (MetaInstr _ k)    n         = getStack k n
 
 algHandler :: Handler o RelevancyStack xs n r a -> SNat (Length xs) -> Vec (Length xs) Bool
-algHandler (Same yes no) (SSucc n) = VCons True (let VCons _ xs = zipRelevancy (VCons False (getStack yes n)) (getStack no (SSucc n)) in xs)
-algHandler (Always k) n = getStack k n
+algHandler (Same _ yes _ no) (SSucc n) = VCons True (let VCons _ xs = zipRelevancy (VCons False (getStack yes n)) (getStack no (SSucc n)) in xs)
+algHandler (Always _ k) n = getStack k n
diff --git a/src/ghc/Parsley/Internal/Backend/CodeGenerator.hs b/src/ghc/Parsley/Internal/Backend/CodeGenerator.hs
--- a/src/ghc/Parsley/Internal/Backend/CodeGenerator.hs
+++ b/src/ghc/Parsley/Internal/Backend/CodeGenerator.hs
@@ -21,7 +21,7 @@
                                             addCoins, refundCoins, drainCoins, giveBursary, blockCoins,
                                             minus, minCoins, maxCoins, zero,
                                             IMVar, IΦVar, IΣVar, MVar(..), ΦVar(..), ΣVar(..), SomeΣVar)
-import Parsley.Internal.Backend.Analysis   (coinsNeeded)
+import Parsley.Internal.Backend.Analysis   (coinsNeeded, shouldInline)
 import Parsley.Internal.Common.Fresh       (VFreshT, HFresh, evalFreshT, evalFresh, construct, MonadFresh(..), mapVFreshT)
 import Parsley.Internal.Common.Indexed     (Fix, Fix4(In4), Cofree(..), Nat(..), imap, histo, extract, (|>))
 import Parsley.Internal.Core.CombinatorAST (Combinator(..), MetaCombinator(..))
@@ -68,12 +68,15 @@
 pattern TryOrElse p q <- (_ :< Try (p :< _)) :<|>: (q :< _)
 
 rollbackHandler :: Handler o (Fix4 (Instr o)) (o : xs) (Succ n) r a
-rollbackHandler = Always (In4 (Seek (In4 Empt)))
+rollbackHandler = Always False (In4 (Seek (In4 Empt)))
 
 parsecHandler :: Fix4 (Instr o) xs (Succ n) r a -> Handler o (Fix4 (Instr o)) (o : xs) (Succ n) r a
-parsecHandler k = Same k (In4 Empt)
+parsecHandler k = Same (not (shouldInline k)) k False (In4 Empt)
 
-altNoCutCompile :: CodeGen o a y -> CodeGen o a x
+recoverHandler :: Fix4 (Instr o) xs n r a -> Handler o (Fix4 (Instr o)) (o : xs) n r a
+recoverHandler = Always . not . shouldInline <*> In4 . Seek
+
+altNoCutCompile :: Trace => CodeGen o a y -> CodeGen o a x
                 -> (forall n xs r. Fix4 (Instr o) xs (Succ n) r a -> Handler o (Fix4 (Instr o)) (o : xs) (Succ n) r a)
                 -> (forall n xs r. Fix4 (Instr o) (x : xs) n r a  -> Fix4 (Instr o) (y : xs) n r a)
                 -> Fix4 (Instr o) (x : xs) (Succ n) r a -> CodeGenStack (Fix4 (Instr o) xs (Succ n) r a)
@@ -108,11 +111,11 @@
      opc <- freshM (runCodeGen op (In4 (_Modify σ (In4 (Jump μ)))))
      freshM (runCodeGen p (In4 (_Make σ (In4 (Iter μ (preOp opc) (parsecHandler (In4 (_Get σ (preM m)))))))))
 
-deep :: Combinator (Cofree Combinator (CodeGen o a)) x -> Maybe (CodeGen o a x)
+deep :: Trace => Combinator (Cofree Combinator (CodeGen o a)) x -> Maybe (CodeGen o a x)
 deep (f :<$>: (p :< _)) = Just $ CodeGen $ \m -> runCodeGen p (In4 (_Fmap (user f) m))
-deep (TryOrElse p q) = Just $ CodeGen $ altNoCutCompile p q (Always . In4 . Seek) id
-deep ((_ :< (Try (p :< _) :$>: x)) :<|>: (q :< _)) = Just $ CodeGen $ altNoCutCompile p q (Always . In4 . Seek) (In4 . Pop . In4 . Push (user x))
-deep ((_ :< (f :<$>: (_ :< Try (p :< _)))) :<|>: (q :< _)) = Just $ CodeGen $ altNoCutCompile p q (Always . In4 . Seek) (In4 . _Fmap (user f))
+deep (TryOrElse p q) = Just $ CodeGen $ altNoCutCompile p q recoverHandler id
+deep ((_ :< (Try (p :< _) :$>: x)) :<|>: (q :< _)) = Just $ CodeGen $ altNoCutCompile p q recoverHandler (In4 . Pop . In4 . Push (user x))
+deep ((_ :< (f :<$>: (_ :< Try (p :< _)))) :<|>: (q :< _)) = Just $ CodeGen $ altNoCutCompile p q recoverHandler (In4 . _Fmap (user f))
 deep (MetaCombinator RequiresCut (_ :< ((p :< _) :<|>: (q :< _)))) = Just $ CodeGen $ \m ->
   do (binder, φ) <- makeΦ m
      pc <- freshΦ (runCodeGen p (deadCommitOptimisation φ))
@@ -126,7 +129,7 @@
 addCoinsNeeded :: Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a
 addCoinsNeeded = coinsNeeded >>= addCoins
 
-shallow :: Combinator (CodeGen o a) x -> Fix4 (Instr o) (x : xs) (Succ n) r a -> CodeGenStack (Fix4 (Instr o) xs (Succ n) r a)
+shallow :: Trace => Combinator (CodeGen o a) x -> Fix4 (Instr o) (x : xs) (Succ n) r a -> CodeGenStack (Fix4 (Instr o) xs (Succ n) r a)
 shallow (Pure x)      m = do return $! In4 (Push (user x) m)
 shallow (Satisfy p)   m = do return $! In4 (Sat (userBool p) m)
 shallow (pf :<*>: px) m = do pxc <- runCodeGen px (In4 (_App m)); runCodeGen pf pxc
@@ -143,7 +146,7 @@
      let np = coinsNeeded pc
      let nm = coinsNeeded m
      -- The minus here is used because the shared coins are propagated out front, neat.
-     return $! In4 (Catch (addCoins (maxCoins (np `minus` nm) zero) (In4 (Tell pc))) (Always (In4 (Seek (In4 (Push (user UNIT) m))))))
+     return $! In4 (Catch (addCoins (maxCoins (np `minus` nm) zero) (In4 (Tell pc))) (Always (not (shouldInline m)) (In4 (Seek (In4 (Push (user UNIT) m))))))
 shallow (Branch b p q) m =
   do (binder, φ) <- makeΦ m
      pc <- freshΦ (runCodeGen p (In4 (Swap (In4 (_App φ)))))
@@ -159,7 +162,7 @@
      let minc = coinsNeeded (In4 (Choices (map userBool fs) qcs defc))
      let defc':qcs' = map (maxCoins zero . (`minus` minc) . coinsNeeded >>= addCoins) (defc:qcs)
      fmap binder (runCodeGen p (In4 (Choices (map user fs) qcs' defc')))
-shallow (Let _ μ _)                  m = do return $! tailCallOptimise μ m
+shallow (Let _ μ)                    m = do return $! tailCallOptimise μ m
 shallow (ChainPre op p)              m = do chainPreCompile op p addCoinsNeeded id m
 shallow (ChainPost p op)             m = do chainPostCompile p op addCoinsNeeded id m
 shallow (MakeRegister σ p q)         m = do qc <- runCodeGen q m; runCodeGen p (In4 (_Make σ qc))
@@ -192,11 +195,9 @@
 freshΦ :: CodeGenStack a -> CodeGenStack a
 freshΦ = newScope
 
--- TODO: We can inline anything that is /pure/ and has no large code foot-print, at the moment this
---       is tripped up by lots of `Push` and `Pop`s.
-makeΦ :: Fix4 (Instr o) (x ': xs) (Succ n) r a -> CodeGenStack (Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a, Fix4 (Instr o) (x : xs) (Succ n) r a)
-makeΦ m | elidable m = return (id, m)
-  where
+makeΦ :: Trace => Fix4 (Instr o) (x ': xs) (Succ n) r a -> CodeGenStack (Fix4 (Instr o) xs (Succ n) r a -> Fix4 (Instr o) xs (Succ n) r a, Fix4 (Instr o) (x : xs) (Succ n) r a)
+makeΦ m | shouldInline m = trace ("eliding " ++ show m) $ return (id, m)
+  {-where
     elidable :: Fix4 (Instr o) (x ': xs) (Succ n) r a -> Bool
     -- This is double-φ optimisation:   If a φ-node points shallowly to another φ-node, then it can be elided
     elidable (In4 (Join _))             = True
@@ -207,7 +208,7 @@
     -- This is a form of double-φ optimisation: If a φ-node points shallowly to a jump, then it can be elided and the jump used instead
     -- Note that this should NOT be done for non-tail calls, as they may generate a large continuation
     elidable (In4 (Pop (In4 (Jump _)))) = True
-    elidable _                          = False
+    elidable _                          = False-}
 makeΦ m = let n = coinsNeeded m in fmap (\φ -> (In4 . MkJoin φ (giveBursary n m), drainCoins n (In4 (Join φ)))) askΦ
 
 freshΣ :: CodeGenStack (ΣVar a)
diff --git a/src/ghc/Parsley/Internal/Backend/Machine/Instructions.hs b/src/ghc/Parsley/Internal/Backend/Machine/Instructions.hs
--- a/src/ghc/Parsley/Internal/Backend/Machine/Instructions.hs
+++ b/src/ghc/Parsley/Internal/Backend/Machine/Instructions.hs
@@ -202,14 +202,18 @@
       captured offset matches the current offset or not.
 
   @since 1.4.0.0 -}
-  Same :: k xs n r a       -- ^ Execute when the input matches, notice that the captured offset is discarded since it is equal to the current.
+  Same :: Bool             -- ^ Whether the input matches handler should generate a binding
+       -> k xs n r a       -- ^ Execute when the input matches, notice that the captured offset is discarded since it is equal to the current.
+       -> Bool             -- ^ Whether the input does not match handler should generate a binding
        -> k (o : xs) n r a -- ^ Execute when the input does not match, the resulting behaviour could use the captured or current input.
        -> Handler o k (o : xs) n r a
   {-| These handlers are unconditional on the input, and will always do the same
       thing regardless of the input provided.
 
-  @since 1.4.0.0 -}
-  Always :: k (o : xs) n r a -> Handler o k (o : xs) n r a
+  @since 1.7.0.0 -}
+  Always :: Bool             -- ^ Whether the handler should generate a binding
+         -> k (o : xs) n r a -- ^ The handler
+         -> Handler o k (o : xs) n r a
 
 {-|
 This determines whether or not an interaction with an register should be materialised
@@ -397,8 +401,8 @@
   imap4 f (MetaInstr m k)     = MetaInstr m (f k)
 
 instance IFunctor4 (Handler o) where
-  imap4 f (Same yes no) = Same (f yes) (f no)
-  imap4 f (Always k)    = Always (f k)
+  imap4 f (Same gyes yes gno no) = Same gyes (f yes) gno (f no)
+  imap4 f (Always gk k)          = Always gk (f k)
 
 instance Show (Fix4 (Instr o) xs n r a) where
   show = ($ "") . getConst4 . cata4 (Const4 . alg)
@@ -432,8 +436,8 @@
       alg (MetaInstr m k)          = "[" . shows m . "] " . getConst4 k
 
 instance Show (Handler o (Const4 (String -> String)) (o : xs) n r a) where
-  show (Same yes no) = "(Dup (Tell (Lift2 same (If " (getConst4 yes (" " (getConst4 no "))))")))
-  show (Always k)    = getConst4 k ""
+  show (Same _ yes _ no) = "(Dup (Tell (Lift2 same (If " (getConst4 yes (" " (getConst4 no "))))")))
+  show (Always _ k)      = getConst4 k ""
 
 instance Show (MetaInstr n) where
   show (AddCoins n)     = "Add " ++ show n ++ " coins"
diff --git a/src/ghc/Parsley/Internal/Backend/Machine/LetRecBuilder.hs b/src/ghc/Parsley/Internal/Backend/Machine/LetRecBuilder.hs
--- a/src/ghc/Parsley/Internal/Backend/Machine/LetRecBuilder.hs
+++ b/src/ghc/Parsley/Internal/Backend/Machine/LetRecBuilder.hs
@@ -1,5 +1,4 @@
-{-# LANGUAGE TupleSections,
-             CPP #-}
+{-# LANGUAGE TupleSections #-}
 {-|
 Module      : Parsley.Internal.Backend.Machine.LetRecBuilder
 Description : Function for building recursive groups.
@@ -19,24 +18,13 @@
 import Data.GADT.Compare                            (GCompare)
 import Data.Some                                    (Some(Some))
 import Language.Haskell.TH                          (newName, Name)
-#if __GLASGOW_HASKELL__ < 900
-import Language.Haskell.TH.Syntax                   (Q, unTypeQ, unsafeTExpCoerce, Exp(VarE, LetE), Dec(FunD), Clause(Clause), Body(NormalB))
-#else
-import Language.Haskell.TH.Syntax                   (unTypeCode, unsafeCodeCoerce, Exp(VarE, LetE), Dec(FunD), Clause(Clause), Body(NormalB))
-#endif
+import Language.Haskell.TH.Syntax                   (Exp(VarE, LetE), Dec(FunD), Clause(Clause), Body(NormalB))
 import Parsley.Internal.Backend.Machine.LetBindings (LetBinding(..), Metadata, Binding, Regs)
+import Parsley.Internal.Backend.Machine.THUtils     (unsafeCodeCoerce, unTypeCode)
 import Parsley.Internal.Backend.Machine.Types       (QSubroutine, qSubroutine, Func)
-
 import Parsley.Internal.Common.Utils                (Code)
 
 import Data.Dependent.Map as DMap (DMap, (!), map, toList, traverseWithKey)
-
-#if __GLASGOW_HASKELL__ < 900
-unsafeCodeCoerce :: Q Exp -> Code a
-unsafeCodeCoerce = unsafeTExpCoerce
-unTypeCode :: Code a -> Q Exp
-unTypeCode = unTypeQ
-#endif
 
 {-|
 Given a collection of bindings, generates a recursive binding group where each is allowed to
diff --git a/src/ghc/Parsley/Internal/Backend/Machine/THUtils.hs b/src/ghc/Parsley/Internal/Backend/Machine/THUtils.hs
new file mode 100644
--- /dev/null
+++ b/src/ghc/Parsley/Internal/Backend/Machine/THUtils.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE CPP #-}
+module Parsley.Internal.Backend.Machine.THUtils (eta, unsafeCodeCoerce, unTypeCode) where
+
+import GHC.Types                     (TYPE)
+#if __GLASGOW_HASKELL__ < 900
+import Language.Haskell.TH.Syntax    (Q, unTypeQ, unsafeTExpCoerce
+#else
+import Language.Haskell.TH.Syntax    (unTypeCode, unsafeCodeCoerce
+#endif
+                                     , Exp(AppE, LamE, VarE), Pat(VarP, BangP, SigP))
+import Parsley.Internal.Common.Utils (Code)
+
+eta :: forall r1 r2 (a :: TYPE r1) (b :: TYPE r2). Code (a -> b) -> Code (a -> b)
+eta = unsafeCodeCoerce . fmap checkEta . unTypeCode
+  where
+    checkEta (LamE [VarP x] (AppE qf (VarE x')))                  | x == x' = qf
+    checkEta (LamE [SigP (VarP x) _] (AppE qf (VarE x')))         | x == x' = qf
+    checkEta (LamE [BangP (VarP x)] (AppE qf (VarE x')))          | x == x' = qf
+    checkEta (LamE [BangP (SigP (VarP x) _)] (AppE qf (VarE x'))) | x == x' = qf
+    checkEta qf                                                             = qf
+
+#if __GLASGOW_HASKELL__ < 900
+unsafeCodeCoerce :: Q Exp -> Code a
+unsafeCodeCoerce = unsafeTExpCoerce
+
+unTypeCode :: Code a -> Q Exp
+unTypeCode = unTypeQ
+#endif
diff --git a/src/ghc/Parsley/Internal/Core/CombinatorAST.hs b/src/ghc/Parsley/Internal/Core/CombinatorAST.hs
--- a/src/ghc/Parsley/Internal/Core/CombinatorAST.hs
+++ b/src/ghc/Parsley/Internal/Core/CombinatorAST.hs
@@ -25,7 +25,7 @@
   Empty          :: Combinator k a
   Try            :: k a -> Combinator k a
   LookAhead      :: k a -> Combinator k a
-  Let            :: Bool -> MVar a -> k a -> Combinator k a
+  Let            :: Bool -> MVar a -> Combinator k a
   NotFollowedBy  :: k a -> Combinator k ()
   Branch         :: k (Either a b) -> k (a -> c) -> k (b -> c) -> Combinator k c
   Match          :: k a -> [Defunc (a -> Bool)] -> [k b] -> k b -> Combinator k b
@@ -55,7 +55,7 @@
   -- | This combinator requires a cut from below to respect parsec semantics
   RequiresCut :: MetaCombinator
   -- | This combinator denotes that within its scope, cut semantics are not enforced
-  -- 
+  --
   -- @since 1.6.0.0
   CutImmune   :: MetaCombinator
 
@@ -70,7 +70,7 @@
   imap _ Empty                = Empty
   imap f (Try p)              = Try (f p)
   imap f (LookAhead p)        = LookAhead (f p)
-  imap f (Let r v p)          = Let r v (f p)
+  imap _ (Let r v)            = Let r v
   imap f (NotFollowedBy p)    = NotFollowedBy (f p)
   imap f (Branch b p q)       = Branch (f b) (f p) (f q)
   imap f (Match p fs qs d)    = Match (f p) fs (map f qs) (f d)
@@ -94,8 +94,8 @@
       alg Empty                                     = "empty"
       alg (Try (Const1 p))                          = "(try " . p . ")"
       alg (LookAhead (Const1 p))                    = "(lookAhead " . p . ")"
-      alg (Let False v _)                           = "(let-bound " . shows v . ")"
-      alg (Let True v _)                            = "(rec " . shows v . ")"
+      alg (Let False v)                             = "(let-bound " . shows v . ")"
+      alg (Let True v)                              = "(rec " . shows v . ")"
       alg (NotFollowedBy (Const1 p))                = "(notFollowedBy " . p . ")"
       alg (Branch (Const1 b) (Const1 p) (Const1 q)) = "(branch " . b . " " . p . " " . q . ")"
       alg (Match (Const1 p) fs qs (Const1 def))     = "(match " . p . " " . shows fs . " [" . intercalateDiff ", " (map getConst1 qs) . "] "  . def . ")"
@@ -135,5 +135,5 @@
 traverseCombinator expose (Debug name p)       = do p' <- expose p; pure (Debug name p')
 traverseCombinator _      (Pure x)             = do pure (Pure x)
 traverseCombinator _      (Satisfy f)          = do pure (Satisfy f)
-traverseCombinator expose (Let r v p)          = do p' <- expose p; pure (Let r v p')
+traverseCombinator _      (Let r v)            = do pure (Let r v)
 traverseCombinator expose (MetaCombinator m p) = do p' <- expose p; pure (MetaCombinator m p')
diff --git a/src/ghc/Parsley/Internal/Frontend/Analysis.hs b/src/ghc/Parsley/Internal/Frontend/Analysis.hs
--- a/src/ghc/Parsley/Internal/Frontend/Analysis.hs
+++ b/src/ghc/Parsley/Internal/Frontend/Analysis.hs
@@ -10,7 +10,7 @@
 @since 1.5.0.0
 -}
 module Parsley.Internal.Frontend.Analysis (
-    analyse, dependencyAnalysis,
+    analyse, dependencyAnalysis, inliner,
     module Flags
   ) where
 
@@ -18,6 +18,7 @@
 import Parsley.Internal.Core.CombinatorAST             (Combinator)
 import Parsley.Internal.Frontend.Analysis.Cut          (cutAnalysis)
 import Parsley.Internal.Frontend.Analysis.Dependencies (dependencyAnalysis)
+import Parsley.Internal.Frontend.Analysis.Inliner      (inliner)
 
 import Parsley.Internal.Frontend.Analysis.Flags as Flags (emptyFlags, AnalysisFlags)
 
diff --git a/src/ghc/Parsley/Internal/Frontend/Analysis/Cut.hs b/src/ghc/Parsley/Internal/Frontend/Analysis/Cut.hs
--- a/src/ghc/Parsley/Internal/Frontend/Analysis/Cut.hs
+++ b/src/ghc/Parsley/Internal/Frontend/Analysis/Cut.hs
@@ -73,7 +73,7 @@
 cutAlg (Pure x) _ = (In (Pure x), False)
 cutAlg (Satisfy f) cut = (mkCut cut (In (Satisfy f)), True)
 cutAlg Empty _ = (In Empty, False)
-cutAlg (Let r μ p) cut = (mkCut (not cut) (In (Let r μ (fst (doCut (ifst p) True)))), False) -- If there is no cut, we generate a piggy for the continuation
+cutAlg (Let r μ) cut = (mkCut (not cut) (In (Let r μ)), False) -- If there is no cut, we generate a piggy for the continuation
 cutAlg (Try p) cut = (In (Try (mkImmune cut (fst (doCut (ifst p) False)))), False)
 -- Special case of below, but we know immunity is useless within `q`
 cutAlg ((p :*: NonComp) :<|>: (q :*: FullPure)) _ = (requiresCut (In (fst (doCut p True) :<|>: fst (doCut q False))), False)
diff --git a/src/ghc/Parsley/Internal/Frontend/Analysis/Dependencies.hs b/src/ghc/Parsley/Internal/Frontend/Analysis/Dependencies.hs
--- a/src/ghc/Parsley/Internal/Frontend/Analysis/Dependencies.hs
+++ b/src/ghc/Parsley/Internal/Frontend/Analysis/Dependencies.hs
@@ -161,9 +161,9 @@
 
 {-# INLINE dependenciesAlg #-}
 dependenciesAlg :: Maybe IMVar -> Combinator Dependencies a -> Dependencies a
-dependenciesAlg (Just v) (Let _ μ@(MVar u) _) = Dependencies $ do unless (u == v) (dependsOn μ)
-dependenciesAlg Nothing  (Let _ μ _)          = Dependencies $ do dependsOn μ
-dependenciesAlg _ p                           = Dependencies $ do traverseCombinator (fmap Const1 . doDependencies) p; return ()
+dependenciesAlg (Just v) (Let _ μ@(MVar u)) = Dependencies $ do unless (u == v) (dependsOn μ)
+dependenciesAlg Nothing  (Let _ μ)          = Dependencies $ do dependsOn μ
+dependenciesAlg _ p                         = Dependencies $ do traverseCombinator (fmap Const1 . doDependencies) p; return ()
 
 dependsOn :: MonadState (Set IMVar) m => MVar a -> m ()
 dependsOn (MVar v) = modify' (insert v)
diff --git a/src/ghc/Parsley/Internal/Frontend/Analysis/Inliner.hs b/src/ghc/Parsley/Internal/Frontend/Analysis/Inliner.hs
new file mode 100644
--- /dev/null
+++ b/src/ghc/Parsley/Internal/Frontend/Analysis/Inliner.hs
@@ -0,0 +1,57 @@
+{-|
+Module      : Parsley.Internal.Frontend.Analysis.Inliner
+Description : Decides whether to inline a let-bound parser.
+License     : BSD-3-Clause
+Maintainer  : Jamie Willis
+Stability   : experimental
+
+Exposes a transformation which can be used to inline let-bindings into their use-sites.
+
+@since 1.7.0.0
+-}
+module Parsley.Internal.Frontend.Analysis.Inliner (inliner) where
+
+import Data.Ratio                          ((%))
+import Parsley.Internal.Common.Indexed     (Fix(..), cata)
+import Parsley.Internal.Core.CombinatorAST (Combinator(..))
+import Parsley.Internal.Core.Identifiers   (MVar)
+
+inlineThreshold :: Rational
+inlineThreshold = 13 % 10
+
+{-|
+Annotate a tree with its cut-points. We assume a cut for let-bound parsers.
+
+@since 1.7.0.0
+-}
+inliner :: Bool -> MVar a -> Fix Combinator a -> Fix Combinator a
+inliner recu _ body | not recu, shouldInline body = body
+inliner recu μ _ = In (Let recu μ)
+
+shouldInline :: Fix Combinator a -> Bool
+shouldInline = (< inlineThreshold) . getWeight . cata (InlineWeight . alg)
+
+newtype InlineWeight a = InlineWeight { getWeight :: Rational }
+
+-- Ideally these should mirror those in the backend inliner, how can we unify them?
+alg :: Combinator InlineWeight a -> Rational
+alg (Pure _)                 = 0
+alg (Satisfy _)              = 1
+alg Empty                    = 0
+alg Let{}                    = 2 % 3
+alg (Try p)                  = getWeight p
+alg (l :<|>: r)              = 1 % 4 + 2 % 5 + getWeight l + getWeight r
+alg (l :<*>: r)              = 1 % 5 + getWeight l + getWeight r
+alg (l :<*: r)               = getWeight l + getWeight r
+alg (l :*>: r)               = getWeight l + getWeight r
+alg (LookAhead c)            = getWeight c
+alg (NotFollowedBy p)        = 1 % 4 + getWeight p
+alg (Debug _ c)              = 2 % 4 + getWeight c
+alg (ChainPre op p)          = 2 % 5 + 6 % 3 + getWeight op + getWeight p
+alg (ChainPost p op)         = 6 % 3 + getWeight op + getWeight p
+alg (Branch b p q)           = 1 % 3 + 2 % 5 + getWeight b + getWeight p + getWeight q
+alg (Match p _ qs def)       = fromIntegral (length qs + 1) % 3 + sum (map getWeight qs) + getWeight def + getWeight p
+alg (MakeRegister _ l r)     = 1 % 3 + getWeight l + getWeight r
+alg (GetRegister _)          = 1 % 3
+alg (PutRegister _ c)        = 1 % 3 + getWeight c
+alg (MetaCombinator _ c)     = getWeight c
diff --git a/src/ghc/Parsley/Internal/Frontend/Compiler.hs b/src/ghc/Parsley/Internal/Frontend/Compiler.hs
--- a/src/ghc/Parsley/Internal/Frontend/Compiler.hs
+++ b/src/ghc/Parsley/Internal/Frontend/Compiler.hs
@@ -2,7 +2,6 @@
 {-# LANGUAGE AllowAmbiguousTypes,
              MagicHash,
              MultiParamTypeClasses,
-             RecursiveDo,
              UndecidableInstances #-}
 {-|
 Module      : Parsley.Internal.Frontend.Compiler
@@ -39,7 +38,7 @@
 import Parsley.Internal.Common.Indexed     (Fix(In), cata, cata', IFunctor(imap), (:+:)(..), (\/), Const1(..))
 import Parsley.Internal.Common.State       (State, get, gets, runState, execState, modify', MonadState)
 import Parsley.Internal.Frontend.Optimiser (optimise)
-import Parsley.Internal.Frontend.Analysis  (analyse, emptyFlags, dependencyAnalysis)
+import Parsley.Internal.Frontend.Analysis  (analyse, emptyFlags, dependencyAnalysis, inliner)
 import Parsley.Internal.Trace              (Trace(trace))
 import System.IO.Unsafe                    (unsafePerformIO)
 
@@ -132,15 +131,14 @@
       let bound = HashSet.member name lets
       let recu = HashSet.member name recs
       if bound || recu then case HashMap.lookup name vs of
-        Just v  -> let μ = MVar v in return $! optimise (Let recu μ (μs DMap.! μ))
+        Just v  -> let μ = MVar v in return $! inliner recu μ (μs DMap.! μ)
         Nothing -> do
           v <- newVar
           let μ = MVar v
           modify' (first (HashMap.insert name v))
-          rec
-            modify' (second (DMap.insert μ q'))
-            q' <- doLetInserter (postprocess q) -- This line should be moved above when there is an inliner pass
-          return $! optimise (Let recu μ q')
+          q' <- doLetInserter (postprocess q)
+          modify' (second (DMap.insert μ q'))
+          return $! inliner recu μ q'
       else do doLetInserter (postprocess q)
 
 postprocess :: Combinator LetInserter a -> LetInserter a
diff --git a/src/ghc/Parsley/Internal/Frontend/Optimiser.hs b/src/ghc/Parsley/Internal/Frontend/Optimiser.hs
--- a/src/ghc/Parsley/Internal/Frontend/Optimiser.hs
+++ b/src/ghc/Parsley/Internal/Frontend/Optimiser.hs
@@ -168,11 +168,4 @@
 optimise (Match (In (Pure x)) fs qs def)                 = foldr (\(f, q) k -> if _val f (_val x) then q else k) def (zip fs qs)
 -- Distributivity Law: f <$> match vs p g def            = match vs p ((f <$>) . g) (f <$> def)
 optimise (f :<$>: (In (Match p fs qs def)))              = In (Match p fs (map (optimise . (f :<$>:)) qs) (optimise (f :<$>: def)))
--- Trivial let-bindings - NOTE: These will get moved when Let nodes no longer have the "source" in them
-optimise (Let False _ p@(In (Pure _)))                               = p
-optimise (Let False _ p@(In Empty))                                  = p
-optimise (Let False _ p@(In (Satisfy _)))                            = p
-optimise (Let False _ p@(In (In (Satisfy _) :$>: _)))                = p
-optimise (Let False _ p@(In (GetRegister _)))                        = p
-optimise (Let False _ p@(In (In (Pure _) :<*>: In (GetRegister _)))) = p
-optimise p                                                           = In p
+optimise p                                               = In p
