diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.1.1 (2024-06-15)
+* add `Apply` combinator (effectively `fmap`)
+* add some more runners and utils (handy for generic-data-functions)
+
 ## 1.1.0 (2024-06-01)
 * add `While` combinator
 * add `Count` combinator
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 [hackage-parsec]: https://hackage.haskell.org/package/parsec
 
 Type level string (`Symbol`) parser combinators. A [Parsec][hackage-parsec]-like
-for `Symbol`s; thus, Symparsec! With all the features you'd expect:
+for `Symbol`s; thus, Symparsec! With many of the features you'd expect:
 
 * define parsers compositionally, largely as you would on the term level
 * pretty, detailed parse errors
diff --git a/src/Symparsec/Example/Expr.hs b/src/Symparsec/Example/Expr.hs
new file mode 100644
--- /dev/null
+++ b/src/Symparsec/Example/Expr.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE UndecidableInstances#-}
+
+{- | Experiments.
+
+Turns out we can't write recursive parsers. But 'P.Apply' can help us write
+handier parsers.
+-}
+
+module Symparsec.Example.Expr where
+
+import Numeric.Natural
+import Symparsec.Parsers qualified as P
+import Symparsec.Parser.While.Predicates qualified as P
+import Symparsec.Parser.Apply qualified as P
+import DeFun.Core
+import DeFun.Function
+
+data Expr
+  = ELit Lit
+  | EBOp Expr BOp Expr
+
+data BOp = Plus
+
+data Lit
+  = LNat Natural
+
+type PLit  = PLNat
+type PLNat = Con1 LNat P.:<$>: P.While P.IsHexDigitSym P.NatHex
+type PELit = Con1 ELit P.:<$>: PLit
+type PEBOp = Curry3Sym (Con3 EBOp) P.:<$>: (PExpr P.:<*>: PBOp P.:<*>: PExpr)
+type PExpr = PELit
+--type PExpr :: PParser (P.OrS (Maybe Natural) (Maybe Natural)) Expr
+--type PExpr = FromEitherSym P.:<$>: (PELit P.:<|>: PEBOp)
+type PBOp  = ConstSym1 Plus P.:<$>: P.Literal "+"
+
+type Curry3Sym
+    :: (a ~> b ~> c ~> r)
+    -> ((a, b), c)
+    ~> r
+data Curry3Sym f abc
+type instance App (Curry3Sym f) '( '(a, b), c) = f @@ a @@ b @@ c
+
+type FromEither :: Either a a -> a
+type family FromEither eaa where
+    FromEither (Right a) = a
+    FromEither (Left  a) = a
+
+type FromEitherSym :: Either a a ~> a
+data FromEitherSym eaa
+type instance App FromEitherSym eaa = FromEither eaa
diff --git a/src/Symparsec/Parser.hs b/src/Symparsec/Parser.hs
--- a/src/Symparsec/Parser.hs
+++ b/src/Symparsec/Parser.hs
@@ -20,7 +20,7 @@
 module Symparsec.Parser
   (
   -- * Parser
-    Parser(..), PParser(..), SParser(..)
+    Parser(..), PParser(..), ResultOf, SParser(..)
   , ParserCh, PParserCh, ParserChSym, ParserChSym1, SParserChSym, SParserChSym1
   , ParserEnd, PParserEnd, ParserEndSym, SParserEndSym
   , ParserSInit, ParserSInitSym, SParserSInitSym
@@ -62,6 +62,9 @@
   , pparserEnd :: ParserEndSym s r
   , pparserS0  :: s
   }
+
+-- | The result type of a type-level parser. (Sometimes handy.)
+type ResultOf (p :: PParser s r) = r
 
 -- | A singled parser.
 --
diff --git a/src/Symparsec/Parser/Apply.hs b/src/Symparsec/Parser/Apply.hs
new file mode 100644
--- /dev/null
+++ b/src/Symparsec/Parser/Apply.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE UndecidableInstances #-}
+
+module Symparsec.Parser.Apply where
+
+import Symparsec.Parser.Common
+
+-- | Apply the given type function to the result.
+--
+-- Effectively 'fmap' for parsers.
+type (:<$>:) :: (r ~> r') -> PParser s r -> PParser s r'
+type family f :<$>: p where
+    f :<$>: 'PParser pCh pEnd s0 = Apply' f pCh pEnd s0
+
+-- unwrapped for instances
+type Apply' f pCh pEnd s0 = 'PParser (ApplyChSym f pCh) (ApplyEndSym f pEnd) s0
+
+-- TODO: Singling is a pain, similar to While. Let's ignore it for now.
+
+type ApplyCh
+    :: (r ~> r')
+    -> ParserChSym s r
+    -> PParserCh s r'
+type family ApplyCh f pCh ch s where
+    ApplyCh f pCh ch s = ApplyCh' f (pCh @@ ch @@ s)
+
+type family ApplyCh' f res where
+    ApplyCh' f (Cont s) = Cont s
+    ApplyCh' f (Done r) = Done (f @@ r)
+    ApplyCh' f (Err  e) = Err e
+
+type ApplyChSym
+    :: (r ~> r')
+    -> ParserChSym s r
+    -> ParserChSym s r'
+data ApplyChSym f pCh x
+type instance App (ApplyChSym f pCh) x = ApplyChSym1 f pCh x
+
+type ApplyChSym1
+    :: (r ~> r')
+    -> ParserChSym  s r
+    -> ParserChSym1 s r'
+data ApplyChSym1 f pCh ch s
+type instance App (ApplyChSym1 f pCh ch) s = ApplyCh f pCh ch s
+
+type ApplyEnd
+    :: (r ~> r')
+    -> ParserEndSym s r
+    -> PParserEnd s r'
+type family ApplyEnd f pEnd s where
+    ApplyEnd f pEnd s = ApplyEnd' f (pEnd @@ s)
+
+type family ApplyEnd' f res where
+    ApplyEnd' f (Right r) = Right (f @@ r)
+    ApplyEnd' f (Left  e) = Left  e
+
+type ApplyEndSym
+    :: (r ~> r')
+    -> ParserEndSym s r
+    -> ParserEndSym s r'
+data ApplyEndSym f pEnd s
+type instance App (ApplyEndSym f pEnd) s = ApplyEnd f pEnd s
diff --git a/src/Symparsec/Run.hs b/src/Symparsec/Run.hs
--- a/src/Symparsec/Run.hs
+++ b/src/Symparsec/Run.hs
@@ -44,6 +44,16 @@
 type RunTest :: PParser s r -> Symbol -> (r, Symbol)
 type RunTest p sym = MapLeftTypeError (Run p sym)
 
+-- | Run the given parser on the given 'Symbol', returning a 'PERun' on failure,
+--   and ignoring any remaining non-consumed characters.
+type Run'_ :: PParser s r -> Symbol -> Either TE.ErrorMessage r
+type Run'_ p str = Run'_Inner (Run' p str)
+
+type Run'_Inner :: Either PERun (a, b) -> Either TE.ErrorMessage a
+type family Run'_Inner eeab where
+    Run'_Inner (Right '(a, b)) = Right a
+    Run'_Inner (Left  e)       = Left (RenderPDoc (PrettyERun e))
+
 -- | Run the singled version of type-level parser on the given 'String',
 --   returning an 'ERun' on failure.
 --
diff --git a/symparsec.cabal b/symparsec.cabal
--- a/symparsec.cabal
+++ b/symparsec.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           symparsec
-version:        1.1.0
+version:        1.1.1
 synopsis:       Type level string parser combinators
 description:    Please see README.md.
 category:       Types, Data
@@ -30,7 +30,9 @@
 library
   exposed-modules:
       Symparsec
+      Symparsec.Example.Expr
       Symparsec.Parser
+      Symparsec.Parser.Apply
       Symparsec.Parser.Common
       Symparsec.Parser.Count
       Symparsec.Parser.End
