diff --git a/bench/MainBenchmarkSuite.hs b/bench/MainBenchmarkSuite.hs
--- a/bench/MainBenchmarkSuite.hs
+++ b/bench/MainBenchmarkSuite.hs
@@ -2,10 +2,11 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Main where
 
+import           Data.Char (isSpace)
 import           Control.DeepSeq
 import           Criterion.Main
 import qualified Criterion.Main as C
-import           Data.Text (unlines, Text, replicate, unpack)
+import           Data.Text (unlines, Text, replicate)
 import           Prelude hiding (unlines)
 import qualified Yi.Rope as F
 
@@ -133,6 +134,23 @@
   , onTextGroup "words" F.words
   ]
 
+spanBreakBench :: [Benchmark]
+spanBreakBench =
+  [ onTextGroup "spanTrue" $ F.span (const True)
+  , onTextGroup "spanFalse" $ F.span (const False)
+  , onTextGroup "spanSpace" $ F.span isSpace
+  , onTextGroup "breakTrue" $ F.break (const True)
+  , onTextGroup "breakFalse" $ F.break (const False)
+  , onTextGroup "breakSpace" $ F.break isSpace
+  ]
+
+foldBench :: [Benchmark]
+foldBench =
+  [ onTextGroup "foldCount" $ F.foldl' (\x _ -> x + 1) (0 :: Integer)
+  , onTextGroup "foldId" $ F.foldl' F.snoc F.empty
+  , onTextGroup "foldReverse" $ F.foldl' (\x y -> F.cons y x) F.empty
+  ]
+
 main :: IO ()
 main = defaultMain $
   [ onIntGroup "drop" F.drop
@@ -157,5 +175,9 @@
   , onTextGroup "any bad, (== '×')" $ F.any (== '×')
   , onTextGroup "all OK (/= '×')" $ F.all (== '×')
   , onTextGroup "all bad, (== '中')" $ F.all (== '中')
+  , onTextGroup "init" F.init
+  , onTextGroup "tail" F.tail
   ] ++ splitBench
     ++ wordsBench
+    ++ spanBreakBench
+    ++ foldBench
diff --git a/src/Yi/Rope.hs b/src/Yi/Rope.hs
--- a/src/Yi/Rope.hs
+++ b/src/Yi/Rope.hs
@@ -44,7 +44,8 @@
    Yi.Rope.intercalate, Yi.Rope.intersperse,
    Yi.Rope.filter, Yi.Rope.map,
    Yi.Rope.words, Yi.Rope.unwords,
-   Yi.Rope.split,
+   Yi.Rope.split, Yi.Rope.init, Yi.Rope.tail,
+   Yi.Rope.span, Yi.Rope.break, Yi.Rope.foldl',
 
    -- * IO
    Yi.Rope.readFile, Yi.Rope.readFile', Yi.Rope.writeFile,
@@ -279,6 +280,22 @@
   EmptyR -> Nothing
   _ :> Chunk _ x -> if TX.null x then Nothing else Just (TX.last x)
 
+-- | Takes every character but the last one: returns Nothing on empty
+-- string.
+init :: YiString -> Maybe YiString
+init (YiString t) = case viewr t of
+  EmptyR -> Nothing
+  ts :> Chunk 0 _ -> Yi.Rope.init (YiString ts)
+  ts :> Chunk l x -> Just . YiString $ ts |- Chunk (l - 1) (TX.init x)
+
+-- | Takes the tail of the underlying string. If the string is empty
+-- to begin with, returns Nothing.
+tail :: YiString -> Maybe YiString
+tail (YiString t) = case viewr t of
+  EmptyR -> Nothing
+  ts :> Chunk 0 _ -> Yi.Rope.tail (YiString ts)
+  ts :> Chunk l x -> Just . YiString $ ts |- Chunk (l - 1) (TX.tail x)
+
 -- | Splits the string at given character position.
 --
 -- If @position <= 0@ then the left string is empty and the right string
@@ -393,6 +410,24 @@
           r = TX.reverse . TX.takeWhile p . TX.reverse $ x
           l' = TX.length r
 
+
+-- | Returns a pair whose first element is the longest prefix
+-- (possibly empty) of t of elements that satisfy p, and whose second
+-- is the remainder of the string. See also 'TX.span'.
+--
+-- This implementation uses 'Yi.Rope.splitAt' which actually is just
+-- as fast as hand-unrolling the tree. GHC sure is great!
+span :: (Char -> Bool) -> YiString -> (YiString, YiString)
+span p y = let x = Yi.Rope.takeWhile p y
+           in case Yi.Rope.splitAt (Yi.Rope.length x) y of
+             -- Re-using ‘x’ seems to gain us a minor performance
+             -- boost.
+             (_, y') -> (x, y')
+
+-- | Just like 'Yi.Rope.span' but with the predicate negated.
+break :: (Char -> Bool) -> YiString -> (YiString, YiString)
+break p = Yi.Rope.span (not . p)
+
 -- | Concatenates the list of 'YiString's after inserting the
 -- user-provided 'YiString' between the elements.
 --
@@ -644,6 +679,18 @@
 -- in itself.
 split :: (Char -> Bool) -> YiString -> [YiString]
 split p = fmap fromText . TX.split p . toText
+
+-- | Left fold.
+--
+-- Benchmarks show that folding is actually Pretty Damn Slow™: consider
+-- whether folding is really the best thing to use in your scenario.
+foldl' :: (a -> Char -> a) -> a -> YiString -> a
+foldl' f a = go a . fromRope
+  where
+    go acc t = case viewl t of
+      EmptyL -> acc
+      Chunk _ x :< ts -> let r = TX.foldl' f acc x
+                         in r `seq` go r ts
 
 -- | Helper function doing conversions of to and from underlying
 -- 'TX.Text'. You should aim to implement everything in terms of
diff --git a/test/Yi/RopeSpec.hs b/test/Yi/RopeSpec.hs
--- a/test/Yi/RopeSpec.hs
+++ b/test/Yi/RopeSpec.hs
@@ -84,3 +84,18 @@
       R.toText (R.unwords (R.fromText <$> t)) `shouldBe` T.unwords t
     prop "\\p -> R.split p ~ T.split p $ isUpper" $ \t ->
       R.toText <$> R.split isUpper (R.fromText t) `shouldBe` T.split isUpper t
+    prop "non-empty s ⊢ R.init s ~ T.init s" $ \t ->
+      let t' = t `T.snoc` 'a' -- ensure non-empty
+      in (fmap R.toText . R.init . R.fromText) t' `shouldBe` (Just . T.init) t'
+    prop "non-empty s ⊢ R.tail s ~ T.tail s" $ \t ->
+      let t' = t `T.snoc` 'a'
+      in (fmap R.toText . R.tail . R.fromText) t' `shouldBe` (Just . T.tail) t'
+    prop "\\p -> R.span p ~ T.span p $ isUpper" $ \t ->
+      let (f, s) `shouldBeT` y = (R.toText f, R.toText s) `shouldBe` y
+      in (R.span isUpper . R.fromText) t `shouldBeT` T.span isUpper t
+    prop "\\p -> R.break p ~ T.break p $ isUpper" $ \t ->
+      let (f, s) `shouldBeT` y = (R.toText f, R.toText s) `shouldBe` y
+      in (R.break isUpper . R.fromText) t `shouldBeT` T.break isUpper t
+    prop "\\p -> R.foldl' p ~ T.foldl' p $ \\x _ -> x + 1" $ \t ->
+      let f x _ = x + (1 :: Integer)
+      in (R.foldl' f 0 . R.fromText) t `shouldBe` T.foldl' f 0 t
diff --git a/yi-rope.cabal b/yi-rope.cabal
--- a/yi-rope.cabal
+++ b/yi-rope.cabal
@@ -1,5 +1,5 @@
 name:                yi-rope
-version:             0.2.1.12
+version:             0.2.2.0
 synopsis:            A rope data structure used by Yi
 description:         A rope data structure used by Yi
 license:             GPL-3
