diff --git a/bench/MainBenchmarkSuite.hs b/bench/MainBenchmarkSuite.hs
--- a/bench/MainBenchmarkSuite.hs
+++ b/bench/MainBenchmarkSuite.hs
@@ -5,7 +5,7 @@
 import           Control.DeepSeq
 import           Criterion.Main
 import qualified Criterion.Main as C
-import           Data.Text (unlines, Text, replicate)
+import           Data.Text (unlines, Text, replicate, unpack)
 import           Prelude hiding (unlines)
 import qualified Yi.Rope as F
 
@@ -85,13 +85,18 @@
 allTexts :: [(Int -> String, [(Int, F.YiString)])]
 allTexts = [longTexts {-, wideTexts, shortTexts, tinyTexts -}]
 
+allChars :: [(Int -> String, [(Int, Char)])]
+allChars = map mkChar "λa"
+  where
+    mkChar c = (\x -> unwords [ "char", [c], show x ], [(1, c)])
+
 -- | Sample usage:
 --
 -- > mkGroup "drop" F.drop allTexts benchOnText
 mkGroup :: String -- ^ Group name
         -> f -- ^ Function being benchmarked
-        -> [(Int -> String, [(Int, F.YiString)])]
-        -> (F.YiString -> String -> f -> Benchmark)
+        -> [(chsize -> String, [(chsize, input)])]
+        -> (input -> String -> f -> Benchmark)
         -> Benchmark
 mkGroup n f subs r = bgroup n tests
   where
@@ -101,6 +106,9 @@
 onTextGroup :: NFData a => String -> (F.YiString -> a) -> Benchmark
 onTextGroup n f = mkGroup n f allTexts benchOnText
 
+onCharGroup :: NFData a => String -> (Char -> a) -> Benchmark
+onCharGroup n f = mkGroup n f allChars benchOnText
+
 onIntGroup :: String -> (Int -> F.YiString -> F.YiString) -> Benchmark
 onIntGroup n f = mkGroup n f allTexts benchTakeDrop
 
@@ -109,14 +117,34 @@
              -> Benchmark
 onSplitGroup n f = mkGroup n f allTexts benchSplitAt
 
+
+splitBench :: [Benchmark]
+splitBench =
+  [ onTextGroup "split none" (F.split (== '×'))
+  , onTextGroup "split lots" (F.split (\x -> x == 'a' || x == 'o'))
+  , onTextGroup "split all" (F.split (const True))
+  ]
+
+wordsBench :: [Benchmark]
+wordsBench =
+  -- The replicate here inflates the benchmark like mad, should be
+  -- moved out.
+  [ onTextGroup "unwords" (\x -> F.unwords (Prelude.replicate 100 x))
+  , onTextGroup "words" F.words
+  ]
+
 main :: IO ()
-main = defaultMain
+main = defaultMain $
   [ onIntGroup "drop" F.drop
   , onIntGroup "take" F.take
+  , onTextGroup "cons" (F.cons 'λ')
+  , onTextGroup "snoc" (`F.snoc` 'λ')
+  , onCharGroup "singleton" F.singleton
   , onTextGroup "countNewLines" F.countNewLines
   , onTextGroup "lines" F.lines
   , onSplitGroup "splitAt" F.splitAt
   , onSplitGroup "splitAtLine" F.splitAtLine
+  , onTextGroup "toReverseString" F.toReverseString
   , onTextGroup "toReverseText" F.toReverseText
   , onTextGroup "toText" F.toText
   , onTextGroup "length" F.length
@@ -125,4 +153,9 @@
   , onTextGroup "empty" $ const F.empty
   , onTextGroup "append" (\x -> F.append x x)
   , onTextGroup "concat x100" $ F.concat . Prelude.replicate 100
-  ]
+  , onTextGroup "any OK, (== '中')" $ F.any (== '中')
+  , onTextGroup "any bad, (== '×')" $ F.any (== '×')
+  , onTextGroup "all OK (/= '×')" $ F.all (== '×')
+  , onTextGroup "all bad, (== '中')" $ F.all (== '中')
+  ] ++ splitBench
+    ++ wordsBench
diff --git a/src/Yi/Rope.hs b/src/Yi/Rope.hs
--- a/src/Yi/Rope.hs
+++ b/src/Yi/Rope.hs
@@ -1,7 +1,5 @@
 {-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE OverloadedStrings #-}
 {-# OPTIONS_HADDOCK show-extensions #-}
 
 -- |
@@ -32,35 +30,48 @@
    Yi.Rope.toString, Yi.Rope.toReverseString,
    Yi.Rope.toText, Yi.Rope.toReverseText,
 
-   -- * List-like functions
+   -- * Functions over content
    Yi.Rope.null, Yi.Rope.empty, Yi.Rope.take, Yi.Rope.drop,
    Yi.Rope.length, Yi.Rope.reverse, Yi.Rope.countNewLines,
-
-   Yi.Rope.lines, Yi.Rope.lines',
+   Yi.Rope.lines, Yi.Rope.lines', Yi.Rope.unlines,
    Yi.Rope.splitAt, Yi.Rope.splitAtLine,
-
+   Yi.Rope.cons, Yi.Rope.snoc, Yi.Rope.singleton,
+   Yi.Rope.head, Yi.Rope.last,
    Yi.Rope.append, Yi.Rope.concat,
+   Yi.Rope.any, Yi.Rope.all,
+   Yi.Rope.dropWhile, Yi.Rope.takeWhile,
+   Yi.Rope.dropWhileEnd, Yi.Rope.takeWhileEnd,
+   Yi.Rope.intercalate, Yi.Rope.intersperse,
+   Yi.Rope.filter, Yi.Rope.map,
+   Yi.Rope.words, Yi.Rope.unwords,
+   Yi.Rope.split,
 
    -- * IO
-   Yi.Rope.readFile, Yi.Rope.writeFile
+   Yi.Rope.readFile, Yi.Rope.readFile', Yi.Rope.writeFile,
 
+   -- * Escape latches to underlying content. Note that these are safe
+   -- to use but it does not mean they should.
+   Yi.Rope.fromRope, Yi.Rope.withText, Yi.Rope.unsafeWithText
+
   ) where
 
 import           Control.Applicative ((<$>))
 import           Control.DeepSeq
 import           Data.Binary
+import           Data.Char (isSpace)
 import qualified Data.FingerTree as T
 import           Data.FingerTree hiding (null, empty, reverse, split)
-import qualified Data.List as L
+import qualified Data.List as L (foldl')
 import           Data.Monoid
 import           Data.String (IsString(..))
 import qualified Data.Text as TX
 import qualified Data.Text.IO as TF (writeFile, readFile)
+import           Prelude hiding (drop)
 
 -- | Used to cache the size of the strings.
 data Size = Indices { charIndex :: {-# UNPACK #-} !Int
                       -- ^ How many characters under here?
-                    , lineIndex :: {-# UNPACK #-} !Int
+                    , lineIndex :: Int
                       -- ^ How many lines under here?
                     } deriving (Eq, Show)
 
@@ -87,18 +98,35 @@
           -> YiChunk -> YiChunk
 overChunk f (Chunk l t) = Chunk l (f t)
 
+countNl :: TX.Text -> Int
+countNl = TX.count (TX.pack "\n")
+
 instance Monoid Size where
   mempty = Indices 0 0
   Indices c l `mappend` Indices c' l' = Indices (c + c') (l + l')
 
 instance Measured Size YiChunk where
-  measure (Chunk l t) = Indices l (TX.count "\n" t)
+  measure (Chunk l t) = Indices l (countNl t)
 
 -- | A 'YiString' is a 'FingerTree' with cached column and line counts
 -- over chunks of 'TX.Text'.
 newtype YiString = YiString { fromRope :: FingerTree Size YiChunk }
-                 deriving (Show, Eq)
+                 deriving (Show)
 
+-- | Two 'YiString's are equal if their underlying text is.
+--
+-- Implementation note: This just uses 'TX.Text' equality as there is
+-- no real opportunity for optimisation here except for a cached
+-- length check first. We could unroll the trees and mess around with
+-- matching prefixes but the overhead would be higher than a simple
+-- conversion and relying on GHC optimisation.
+--
+-- The derived Eq implementation for the underlying tree only passes
+-- the equality check if the chunks are the same too which is not what
+-- we want.
+instance Eq YiString where
+  t == t' = Yi.Rope.length t == Yi.Rope.length t' && toText t == toText t'
+
 instance NFData Size where
   rnf (Indices !c !l) = c `seq` l `seq` ()
 
@@ -108,6 +136,17 @@
 instance NFData YiString where
   rnf = rnf . toText
 
+instance IsString YiString where
+  fromString = Yi.Rope.fromString
+
+instance Monoid YiString where
+  mempty = Yi.Rope.empty
+  mappend = Yi.Rope.append
+  mconcat = Yi.Rope.concat
+
+instance Ord YiString where
+  compare x y = toText x `compare` toText y
+
 (-|) :: YiChunk -> FingerTree Size YiChunk -> FingerTree Size YiChunk
 b -| t | chunkSize b == 0 = t
        | otherwise        = b <| t
@@ -150,8 +189,12 @@
 toString = TX.unpack . toText
 
 -- | See 'toReverseText'.
+--
+-- Note that it is actually ~4.5 times faster to use 'toReverseText'
+-- and unpack the result than to convert to 'String' and use
+-- 'Prelude.reverse'.
 toReverseString :: YiString -> String
-toReverseString = Prelude.reverse . toString
+toReverseString = TX.unpack . toReverseText
 
 -- | This is like 'fromText' but it allows the user to specify the
 -- chunk size to be used. Uses 'defaultChunkSize' if the given
@@ -196,9 +239,6 @@
 toReverseText :: YiString -> TX.Text
 toReverseText = TX.reverse . toText
 
-instance IsString YiString where
-  fromString = Yi.Rope.fromString
-
 -- | Checks if the given 'YiString' is actually empty.
 null :: YiString -> Bool
 null = T.null . fromRope
@@ -227,6 +267,18 @@
 concat :: [YiString] -> YiString
 concat = L.foldl' append empty
 
+-- | Take the first character of the underlying string if possible.
+head :: YiString -> Maybe Char
+head (YiString t) = case viewl t of
+  EmptyL -> Nothing
+  Chunk _ x :< _ -> if TX.null x then Nothing else Just (TX.head x)
+
+-- | Take the last character of the underlying string if possible.
+last :: YiString -> Maybe Char
+last (YiString t) = case viewr t of
+  EmptyR -> Nothing
+  _ :> Chunk _ x -> if TX.null x then Nothing else Just (TX.last x)
+
 -- | Splits the string at given character position.
 --
 -- If @position <= 0@ then the left string is empty and the right string
@@ -243,17 +295,19 @@
 -- chunk of the right side of the split. We do precisely that.
 --
 -- All together, this split is only as expensive as underlying
--- 'T.split', the cost of splitting a chunk into two and the cost
--- consing and snocing one chunk to each string. As the chunks are
--- short, the split fairly cheap and cons/snoc constant time, this
--- turns out pretty fast all together.
+-- 'T.split', the cost of splitting a chunk into two, the cost of one
+-- cons and one cons of a chunk and lastly the cost of 'T.splitAt' of
+-- the underlying 'TX.Text'. It turns out to be fairly fast all
+-- together.
 splitAt :: Int -> YiString -> (YiString, YiString)
-splitAt n (YiString t) = case viewl s of
-  Chunk _ x :< ts | n' /= 0 ->
-    let (lx, rx) = TX.splitAt n' x
-    in (YiString $ f |> mkChunk TX.length lx,
-        YiString $ mkChunk TX.length rx -| ts)
-  _ -> (YiString f, YiString s)
+splitAt n (YiString t)
+  | n <= 0 = (mempty, YiString t)
+  | otherwise = case viewl s of
+    Chunk l x :< ts | n' /= 0 ->
+      let (lx, rx) = TX.splitAt n' x
+      in (YiString $ f |> Chunk n' lx,
+          YiString $ Chunk (l - n') rx -| ts)
+    _ -> (YiString f, YiString s)
   where
     (f, s) = T.split ((> n) . charIndex) t
     n' = n - charIndex (measure f)
@@ -266,6 +320,138 @@
 drop :: Int -> YiString -> YiString
 drop n = snd . Yi.Rope.splitAt n
 
+-- | The usual 'Prelude.dropWhile' optimised for 'YiString's.
+dropWhile :: (Char -> Bool) -> YiString -> YiString
+dropWhile p = YiString . go . fromRope
+  where
+    go t = case viewl t of
+      EmptyL -> T.empty
+      Chunk l x :< ts ->
+        let r = TX.dropWhile p x
+            l' = TX.length r
+        in case compare l' l of
+          -- We dropped nothing so we must be done.
+          EQ -> t
+          -- We dropped something, if it was everything then drop from
+          -- next chunk.
+          LT | TX.null r -> go ts
+          -- It wasn't everything and we have left-overs, we must be done.
+             | otherwise -> Chunk l' r <| ts
+          -- We shouldn't really get here or it would mean that
+          -- dropping stuff resulted in more content than we had. This
+          -- can only happen if unsafe functions don't preserve the
+          -- chunk size and it goes out of sync with the text length.
+          -- Preserve this abomination, it may be useful for
+          -- debugging.
+          _ -> Chunk l' r -| ts
+
+-- | As 'Yi.Rope.dropWhile' but drops from the end instead.
+dropWhileEnd :: (Char -> Bool) -> YiString -> YiString
+dropWhileEnd p = YiString . go . fromRope
+  where
+    go t = case viewr t of
+      EmptyR -> T.empty
+      ts :> Chunk l x ->
+        let r = TX.dropWhileEnd p x
+            l' = TX.length r
+        in case compare l' l of
+          EQ -> t
+          LT | TX.null r -> go ts
+             | otherwise -> ts |> Chunk l' r
+          _ -> ts |- Chunk l' r
+
+-- | The usual 'Prelude.takeWhile' optimised for 'YiString's.
+takeWhile :: (Char -> Bool) -> YiString -> YiString
+takeWhile p = YiString . go . fromRope
+  where
+    go t = case viewl t of
+      EmptyL -> T.empty
+      Chunk l x :< ts ->
+        let r = TX.takeWhile p x
+            l' = TX.length r
+        in case compare l' l of
+          -- We took the whole chunk, keep taking more.
+          EQ -> Chunk l x <| go ts
+          -- We took some stuff but not everything so we're done.
+          -- Alternatively, we took more than the size chunk so
+          -- preserve this wonder. This should only ever happen if you
+          -- use unsafe functions and Chunk size goes out of sync with
+          -- actual text length.
+          _ -> Chunk l' r <| ts
+
+-- | Like 'Yi.Rope.takeWhile' but takes from the end instead.
+takeWhileEnd :: (Char -> Bool) -> YiString -> YiString
+takeWhileEnd p = YiString . go . fromRope
+  where
+    go t = case viewr t of
+      EmptyR -> T.empty
+      ts :> Chunk l x -> case compare l' l of
+        EQ -> go ts |> Chunk l x
+        _ -> ts |> Chunk l' r
+        where
+          -- no TX.takeWhileEnd – https://github.com/bos/text/issues/89
+          r = TX.reverse . TX.takeWhile p . TX.reverse $ x
+          l' = TX.length r
+
+-- | Concatenates the list of 'YiString's after inserting the
+-- user-provided 'YiString' between the elements.
+--
+-- Empty 'YiString's are not ignored and will end up as strings of
+-- length 1. If you don't want this, it's up to you to pre-process the
+-- list. Just as with 'Yi.Rope.intersperse', it is up to the user to
+-- pre-process the list.
+intercalate :: YiString -> [YiString] -> YiString
+intercalate _ [] = mempty
+intercalate (YiString t') ts = YiString $ t' >< go ts
+  where
+    go []                = mempty
+    go (YiString t : ts') = t >< t' >< go ts'
+
+-- | Intersperses the given character between the 'YiString's. This is
+-- useful when you have a bunch of strings you just want to separate
+-- something with, comma or a dash. Note that it only inserts the
+-- character between the elements.
+--
+-- What's more, the result is a single 'YiString'. You can easily
+-- achieve a version that blindly inserts elements to the back by
+-- mapping over the list instead of using this function.
+--
+-- You can think of it as a specialised version of
+-- 'Yi.Rope.intercalate'. Note that what this does __not__ do is
+-- intersperse characters into the underlying text, you should convert
+-- and use 'TX.intersperse' for that instead.
+intersperse :: Char -> [YiString] -> YiString
+intersperse _ [] = mempty
+intersperse c (t:ts) = t <> go ts
+  where
+    go [] = mempty
+    go (t':ts') = (c `cons` t') <> go ts'
+
+-- | Add a 'Char' in front of a 'YiString'.
+--
+-- We add the character to the front of the first chunk. This does
+-- mean that a lot of 'cons' might result in an abnormally large first
+-- chunk so if you have to do that, consider using 'append' instead..
+cons :: Char -> YiString -> YiString
+cons c (YiString t) = case viewl t of
+  EmptyL -> Yi.Rope.singleton c
+  Chunk !l x :< ts -> YiString $ Chunk (l + 1) (c `TX.cons` x) <| ts
+
+-- | Add a 'Char' in the back of a 'YiString'.
+--
+-- We add the character to the end of the last chunk. This does mean
+-- that a lot of 'snoc' might result in an abnormally large last chunk
+-- so if you have to do that, consider using 'append' instead..
+snoc :: YiString -> Char -> YiString
+snoc (YiString t) c = case viewr t of
+  EmptyR -> Yi.Rope.singleton c
+  ts :> Chunk l x -> YiString $ ts |> Chunk (l + 1) (x `TX.snoc` c)
+
+-- | Single character 'YiString'. Consider whether it's worth creating
+-- this, maybe you can use 'cons' or 'snoc' instead?
+singleton :: Char -> YiString
+singleton c = YiString . T.singleton $ Chunk 1 (TX.singleton c)
+
 -- | Splits the underlying string before the given line number.
 -- Zero-indexed lines.
 --
@@ -300,7 +486,7 @@
     cutExcess :: Int -> TX.Text -> (TX.Text, TX.Text)
     cutExcess n t = case TX.length t of
       0 -> (TX.empty, TX.empty)
-      _ -> let ns = TX.count "\n" t
+      _ -> let ns = countNl t
                ls = TX.lines t
                front = TX.unlines $ Prelude.take (ns - n) ls
                back = TX.drop (TX.length front) t
@@ -320,16 +506,16 @@
 -- result back together which was inconsistent with the rest of the
 -- interface which worked with number of newlines.
 lines :: YiString -> [YiString]
-lines = map dropNl . lines'
+lines = Prelude.map dropNl . lines'
   where
-    dropNl (YiString t) = case viewr t of
+    dropNl (YiString t)  = case viewr t of
+      EmptyR -> Yi.Rope.empty
       ts :> ch@(Chunk l tx) ->
         YiString $ ts |- if TX.null tx
                          then ch
                          else case TX.last tx of
                            '\n' -> Chunk (l - 1) (TX.init tx)
                            _ -> ch
-      EmptyR -> YiString T.empty
 
 -- | Splits the 'YiString' into a list of 'YiString' each containing a
 -- line.
@@ -352,14 +538,141 @@
               then if T.null f then [] else [YiString f]
               else YiString f : lines' (YiString s)
 
+-- | Joins up lines by a newline character. It does not leave a
+-- newline after the last line. If you want a more classical
+-- 'Prelude.unlines' behaviour, use 'Yi.Rope.map' along with
+-- 'Yi.Rope.snoc'.
+unlines :: [YiString] -> YiString
+unlines = Yi.Rope.intersperse '\n'
+
+-- | 'YiString' specialised @any@.
+--
+-- Implementation note: this currently just does any by doing ‘TX.Text’
+-- conversions upon consecutive chunks. We should be able to speed it
+-- up by running it in parallel over multiple chunks.
+any :: (Char -> Bool) -> YiString -> Bool
+any p = go . fromRope
+  where
+    go x = case viewl x of
+      EmptyL -> False
+      Chunk _ t :< ts -> TX.any p t || go ts
+
+-- | 'YiString' specialised @all@.
+--
+-- See the implementation note for 'Yi.Rope.any'.
+all :: (Char -> Bool) -> YiString -> Bool
+all p = go . fromRope
+  where
+    go x = case viewl x of
+      EmptyL -> False
+      Chunk _ t :< ts -> TX.all p t || go ts
+
 -- | To serialise a 'YiString', we turn it into a regular 'String'
 -- first.
 instance Binary YiString where
   put = put . toString
   get = Yi.Rope.fromString <$> get
 
+-- | Write a 'YiString' into the given file. It's up to the user to
+-- handle exceptions.
 writeFile :: FilePath -> YiString -> IO ()
 writeFile f = TF.writeFile f . toText
 
+-- | Reads file into the rope, using 'fromText'. It's up to the user
+-- to handle exceptions.
 readFile :: FilePath -> IO YiString
 readFile f = fromText <$> TF.readFile f
+
+-- | A version of 'readFile' which allows for arbitrary chunk size to
+-- start with.
+--
+-- For example, @readFile' foo ((/ 2) . 'TX.length')@ would produce
+-- chunks that are half the size of the read in text: whether that's a
+-- good idea depends on situation.
+--
+-- Note that if this number ends up as @< 1@, 'defaultChunkSize' will
+-- be used instead.
+--
+-- It's up to the user to handle exceptions.
+readFile' :: FilePath -> (TX.Text -> Int) -> IO YiString
+readFile' f l = do
+  c <- TF.readFile f
+  let l' = case l c of
+        x | x < 1     -> defaultChunkSize
+          | otherwise -> x
+  return $ fromText' l' c
+
+-- | Filters the characters from the underlying string.
+--
+-- >>> filter (/= 'a') "bac"
+-- "bc"
+filter :: (Char -> Bool) -> YiString -> YiString
+filter p = YiString . go . fromRope
+  where
+    go t = case viewl t of
+      EmptyL -> T.empty
+      Chunk _ x :< ts -> mkChunk TX.length (TX.filter p x) -| go ts
+
+-- | Maps the characters over the underlying string.
+map :: (Char -> Char) -> YiString -> YiString
+map f = YiString . go . fromRope
+  where
+    go t = case viewl t of
+      EmptyL -> T.empty
+      Chunk l x :< ts -> Chunk l (TX.map f x) <| go ts
+
+-- | Join given 'YiString's with a space. Empty lines will be filtered
+-- out first.
+unwords :: [YiString] -> YiString
+unwords = Yi.Rope.intersperse ' '
+
+-- | Splits the given 'YiString' into a list of words, where spaces
+-- are determined by 'isSpace'. No empty strings are in the result
+-- list.
+words :: YiString -> [YiString]
+words = Prelude.filter (not . Yi.Rope.null) . Yi.Rope.split isSpace
+
+-- | Splits the 'YiString' on characters matching the predicate, like
+-- 'TX.split'.
+--
+-- For splitting on newlines use 'Yi.Rope.lines' or 'Yi.Rope.lines''
+-- instead.
+--
+-- Implementation note: GHC actually makes this naive implementation
+-- about as fast and in cases with lots of splits, faster, as a
+-- hand-rolled version on chunks with appends which is quite amazing
+-- in itself.
+split :: (Char -> Bool) -> YiString -> [YiString]
+split p = fmap fromText . TX.split p . toText
+
+-- | Helper function doing conversions of to and from underlying
+-- 'TX.Text'. You should aim to implement everything in terms of
+-- 'YiString' instead.
+--
+-- Please note that this maps over each __chunk__ so this can only be
+-- used with layout-agnostic functions. For example
+--
+-- >>> let t = 'fromString' "abc" <> 'fromString' "def"
+-- >>> 'toString' $ 'withText' 'TX.reverse' t
+-- "cbafed"
+--
+-- Probably doesn't do what you wanted, but 'TX.toUpper' would.
+-- Specifically, for any @f : 'TX.Text' → 'TX.Text'@, 'withText' will
+-- only do the ‘expected’ thing iff
+--
+-- @f x <> f y ≡ f (x <> y)@
+--
+-- which should look very familiar.
+withText :: (TX.Text -> TX.Text) -> YiString -> YiString
+withText f = YiString . T.fmap' (mkChunk TX.length . f . _fromChunk) . fromRope
+
+-- | Maps over each __chunk__ which means this function is UNSAFE! If
+-- you use this with functions which don't preserve 'Size', that is
+-- the chunk length and number of newlines, things will break really,
+-- really badly. You should not need to use this.
+--
+-- Also see 'T.unsafeFmap'
+unsafeWithText :: (TX.Text -> TX.Text) -> YiString -> YiString
+unsafeWithText f = YiString . T.unsafeFmap g . fromRope
+  where
+    g (Chunk l t) = Chunk l (f t)
diff --git a/test/Yi/RopeSpec.hs b/test/Yi/RopeSpec.hs
--- a/test/Yi/RopeSpec.hs
+++ b/test/Yi/RopeSpec.hs
@@ -1,6 +1,8 @@
 {-# LANGUAGE OverloadedStrings #-}
 module Yi.RopeSpec (main, spec) where
 
+import           Control.Applicative
+import           Data.Char (isUpper, toUpper, isSpace)
 import qualified Data.Text as T
 import           Test.Hspec
 import           Test.Hspec.QuickCheck
@@ -45,3 +47,40 @@
       R.append (R.fromText t) `isLikeT` T.append t
     prop "R.concat ~ T.concat" $ \s ->
       (R.toText . R.concat . map R.fromText) s `shouldBe` T.concat s
+    prop "R.head ~ T.head" $ R.head `isLike` (\x -> if T.null x
+                                                    then Nothing
+                                                    else Just (T.head x))
+    prop "R.last ~ T.last" $ R.last `isLike` (\x -> if T.null x
+                                                    then Nothing
+                                                    else Just (T.last x))
+    prop "R.cons ~ T.cons" $ \c -> R.cons c `isLikeT` T.cons c
+    prop "R.snoc ~ T.snoc" $ \c -> R.cons c `isLikeT` T.cons c
+    prop "R.singleton ~ T.singleton" $
+      \c -> (R.toText . R.singleton) c `shouldBe` T.singleton c
+    prop "\\p -> R.any p ~ T.any p $ const True" $ \t ->
+      R.any (const True) (R.fromText t) `shouldBe` T.any (const True) t
+    prop "\\p -> R.any p ~ T.any p $ const False" $ \t ->
+      R.any (const False) (R.fromText t) `shouldBe` T.any (const False) t
+    prop "\\f -> R.withText ~ f $ T.toTitle" $
+      R.withText T.toTitle `isLikeT` T.toTitle
+    prop "\\p -> R.dropWhile p ~ T.dropWhile p $ isUpper" $
+      R.dropWhile isUpper `isLikeT` T.dropWhile isUpper
+    prop "\\p -> R.takeWhile p ~ T.takeWhile p $ isUpper" $
+      R.takeWhile isUpper `isLikeT` T.takeWhile isUpper
+    prop "R.compare ~ T.compare" $ \t t' ->
+      compare (R.fromText t) (R.fromText t') `shouldBe` compare t t'
+    prop "\\p -> R.filter p ~ T.filter p $ isUpper" $
+      R.filter isUpper `isLikeT` T.filter isUpper
+    prop "\\f -> R.map f ~ T.map f $ toUpper" $
+      R.map toUpper `isLikeT` T.map toUpper
+    prop "\\p -> R.dropWhileEnd p ~ T.dropWhileEnd p $ isSpace" $
+      R.dropWhileEnd isSpace `isLikeT` T.dropWhileEnd isSpace
+    prop "\\p -> R.takeWhileEnd p ~ rev . T.takeWhile p . rev $ isSpace" $
+      R.takeWhileEnd isSpace
+      `isLikeT` T.reverse . T.takeWhile isSpace . T.reverse
+    prop "R.words ~ T.words" $ \t ->
+      R.toText <$> R.words (R.fromText t) `shouldBe` T.words t
+    prop "R.unwords ~ T.unwords" $ \t ->
+      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
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.0.0
+version:             0.2.1.11
 synopsis:            A rope data structure used by Yi
 description:         A rope data structure used by Yi
 license:             GPL-3
@@ -11,6 +11,7 @@
 cabal-version:       >=1.10
 
 library
+  ghc-options:      -fpedantic-bottoms -flate-dmd-anal -fexpose-all-unfoldings -Wall -O2
   exposed-modules:
     Yi.Rope
 
