diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,11 @@
 Changelog for Extra
 
+1.7.2, released 2020-05-25
+    #56, add zipWithLongest
+    #57, make duration in MonadIO
+    Simplify and optimise Barrier
+    Mark modules that are empty as DEPRECATED
+    Remove support for GHC 7.10
 1.7.1, released 2020-03-10
     Add NOINLINE to errorIO to work around a GHC 8.4 bug
 1.7, released 2020-03-05
diff --git a/extra.cabal b/extra.cabal
--- a/extra.cabal
+++ b/extra.cabal
@@ -1,7 +1,7 @@
 cabal-version:      >= 1.18
 build-type:         Simple
 name:               extra
-version:            1.7.1
+version:            1.7.2
 license:            BSD3
 license-file:       LICENSE
 category:           Development
@@ -15,7 +15,7 @@
     The module "Extra" documents all functions provided by this library. Modules such as "Data.List.Extra" provide extra functions over "Data.List" and also reexport "Data.List". Users are recommended to replace "Data.List" imports with "Data.List.Extra" if they need the extra functionality.
 homepage:           https://github.com/ndmitchell/extra#readme
 bug-reports:        https://github.com/ndmitchell/extra/issues
-tested-with:        GHC==8.8.1, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2, GHC==7.10.3
+tested-with:        GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4, GHC==8.2.2, GHC==8.0.2
 
 extra-doc-files:
     CHANGES.txt
@@ -31,13 +31,11 @@
     default-language: Haskell2010
     hs-source-dirs: src
     build-depends:
-        base >= 4.8 && < 5,
+        base >= 4.9 && < 5,
         directory,
         filepath,
         process,
         clock >= 0.7,
-        -- For GHC 7.10 since Data.List.NonEmpty wasn't in base then
-        semigroups,
         time
     if !os(windows)
         build-depends: unix
diff --git a/src/Control/Concurrent/Extra.hs b/src/Control/Concurrent/Extra.hs
--- a/src/Control/Concurrent/Extra.hs
+++ b/src/Control/Concurrent/Extra.hs
@@ -182,38 +182,26 @@
 --   for it to complete. A barrier has similarities to a future or promise
 --   from other languages, has been known as an IVar in other Haskell work,
 --   and in some ways is like a manually managed thunk.
-newtype Barrier a = Barrier (Var (Either (MVar ()) a))
-    -- Either a Left empty MVar you should wait or a Right result
-    -- With base 4.7 and above readMVar is atomic so you probably can implement Barrier directly on MVar a
+newtype Barrier a = Barrier (MVar a)
 
 -- | Create a new 'Barrier'.
 newBarrier :: IO (Barrier a)
-newBarrier = fmap Barrier $ newVar . Left =<< newEmptyMVar
+newBarrier = Barrier <$> newEmptyMVar
 
 -- | Write a value into the Barrier, releasing anyone at 'waitBarrier'.
 --   Any subsequent attempts to signal the 'Barrier' will throw an exception.
 signalBarrier :: Partial => Barrier a -> a -> IO ()
-signalBarrier (Barrier var) v = mask_ $ -- use mask so never in an inconsistent state
-    join $ modifyVar var $ \x -> case x of
-        Left bar -> pure (Right v, putMVar bar ())
-        Right res -> error "Control.Concurrent.Extra.signalBarrier, attempt to signal a barrier that has already been signaled"
+signalBarrier (Barrier var) v = do
+    b <- tryPutMVar var v
+    unless b $ errorIO "Control.Concurrent.Extra.signalBarrier, attempt to signal a barrier that has already been signaled"
 
 
 -- | Wait until a barrier has been signaled with 'signalBarrier'.
 waitBarrier :: Barrier a -> IO a
-waitBarrier (Barrier var) = do
-    x <- readVar var
-    case x of
-        Right res -> pure res
-        Left bar -> do
-            readMVar bar
-            x <- readVar var
-            case x of
-                Right res -> pure res
-                Left bar -> error "Control.Concurrent.Extra, internal invariant violated in Barrier"
+waitBarrier (Barrier var) = readMVar var
 
 
 -- | A version of 'waitBarrier' that never blocks, returning 'Nothing'
 --   if the barrier has not yet been signaled.
 waitBarrierMaybe :: Barrier a -> IO (Maybe a)
-waitBarrierMaybe (Barrier bar) = eitherToMaybe <$> readVar bar
+waitBarrierMaybe (Barrier bar) = tryReadMVar bar
diff --git a/src/Data/List/Extra.hs b/src/Data/List/Extra.hs
--- a/src/Data/List/Extra.hs
+++ b/src/Data/List/Extra.hs
@@ -31,7 +31,7 @@
     disjoint, allSame, anySame,
     repeatedly, firstJust,
     concatUnzip, concatUnzip3,
-    zipFrom, zipWithFrom,
+    zipFrom, zipWithFrom, zipWithLongest,
     replace, merge, mergeBy,
     ) where
 
@@ -769,3 +769,17 @@
 balance a x (T R b y (T R c z d)) = T R (T B a x b) y (T B c z d)
 balance a x (T R (T R b y c) z d) = T R (T B a x b) y (T B c z d)
 balance a x b = T B a x b
+
+
+-- | Like 'zipWith', but keep going to the longest value. The function
+--   argument will always be given at least one 'Just', and while both
+--   lists have items, two 'Just' values.
+--
+-- > zipWithLongest (,) "a" "xyz" == [(Just 'a', Just 'x'), (Nothing, Just 'y'), (Nothing, Just 'z')]
+-- > zipWithLongest (,) "a" "x" == [(Just 'a', Just 'x')]
+-- > zipWithLongest (,) "" "x" == [(Nothing, Just 'x')]
+zipWithLongest :: (Maybe a -> Maybe b -> c) -> [a] -> [b] -> [c]
+zipWithLongest f [] [] = []
+zipWithLongest f (x:xs) (y:ys) = f (Just x) (Just y) : zipWithLongest f xs ys
+zipWithLongest f [] ys = map (f Nothing . Just) ys
+zipWithLongest f xs [] = map ((`f` Nothing) . Just) xs
diff --git a/src/Data/Typeable/Extra.hs b/src/Data/Typeable/Extra.hs
--- a/src/Data/Typeable/Extra.hs
+++ b/src/Data/Typeable/Extra.hs
@@ -3,7 +3,7 @@
 --   The package also exports the existing "Data.Typeable" functions.
 --
 --   Currently this module has no functionality beyond "Data.Typeable".
-module Data.Typeable.Extra(
+module Data.Typeable.Extra {-# DEPRECATED "Use Data.Typeable directly" #-} (
     module Data.Typeable
     ) where
 
diff --git a/src/Extra.hs b/src/Extra.hs
--- a/src/Extra.hs
+++ b/src/Extra.hs
@@ -23,7 +23,7 @@
     writeIORef', atomicWriteIORef', atomicModifyIORef_, atomicModifyIORef'_,
     -- * Data.List.Extra
     -- | Extra functions available in @"Data.List.Extra"@.
-    lower, upper, trim, trimStart, trimEnd, word1, line1, escapeHTML, escapeJSON, unescapeHTML, unescapeJSON, dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd', takeWhileEnd, stripSuffix, stripInfix, stripInfixEnd, dropPrefix, dropSuffix, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, headDef, lastDef, notNull, list, unsnoc, cons, snoc, drop1, dropEnd1, mconcatMap, enumerate, groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, nubSort, nubSortBy, nubSortOn, maximumOn, minimumOn, disjoint, allSame, anySame, repeatedly, firstJust, concatUnzip, concatUnzip3, zipFrom, zipWithFrom, replace, merge, mergeBy,
+    lower, upper, trim, trimStart, trimEnd, word1, line1, escapeHTML, escapeJSON, unescapeHTML, unescapeJSON, dropEnd, takeEnd, splitAtEnd, breakEnd, spanEnd, dropWhileEnd', takeWhileEnd, stripSuffix, stripInfix, stripInfixEnd, dropPrefix, dropSuffix, wordsBy, linesBy, breakOn, breakOnEnd, splitOn, split, chunksOf, headDef, lastDef, notNull, list, unsnoc, cons, snoc, drop1, dropEnd1, mconcatMap, enumerate, groupSort, groupSortOn, groupSortBy, nubOrd, nubOrdBy, nubOrdOn, nubOn, groupOn, nubSort, nubSortBy, nubSortOn, maximumOn, minimumOn, disjoint, allSame, anySame, repeatedly, firstJust, concatUnzip, concatUnzip3, zipFrom, zipWithFrom, zipWithLongest, replace, merge, mergeBy,
     -- * Data.List.NonEmpty.Extra
     -- | Extra functions available in @"Data.List.NonEmpty.Extra"@.
     (|:), (|>), appendl, appendr, maximum1, minimum1, maximumBy1, minimumBy1, maximumOn1, minimumOn1,
diff --git a/src/System/Environment/Extra.hs b/src/System/Environment/Extra.hs
--- a/src/System/Environment/Extra.hs
+++ b/src/System/Environment/Extra.hs
@@ -1,9 +1,8 @@
 
--- | Extra functions for "System.Environment". All these functions are available in later GHC versions,
---   but this code works all the way back to GHC 7.2.
+-- | Extra functions for "System.Environment".
 --
 --   Currently this module has no functionality beyond "System.Environment".
-module System.Environment.Extra(
+module System.Environment.Extra  {-# DEPRECATED "Use System.Environment directly" #-} (
     module System.Environment,
     ) where
 
diff --git a/src/System/Time/Extra.hs b/src/System/Time/Extra.hs
--- a/src/System/Time/Extra.hs
+++ b/src/System/Time/Extra.hs
@@ -15,6 +15,7 @@
 import Control.Concurrent
 import System.Clock
 import Numeric.Extra
+import Control.Monad.IO.Class
 import Control.Monad.Extra
 import Control.Exception.Extra
 import Data.Typeable
@@ -108,9 +109,9 @@
 -- | Record how long a computation takes in 'Seconds'.
 --
 -- > do (a,_) <- duration $ sleep 1; pure $ a >= 1 && a <= 1.5
-duration :: IO a -> IO (Seconds, a)
+duration :: MonadIO m => m a -> m (Seconds, a)
 duration act = do
-    time <- offsetTime
+    time <- liftIO offsetTime
     res <- act
-    time <- time
+    time <- liftIO time
     pure (time, res)
diff --git a/src/Text/Read/Extra.hs b/src/Text/Read/Extra.hs
--- a/src/Text/Read/Extra.hs
+++ b/src/Text/Read/Extra.hs
@@ -2,7 +2,7 @@
 -- | This module provides "Text.Read" with functions added in later versions.
 --
 --   Currently this module has no functionality beyond "Text.Read".
-module Text.Read.Extra(
+module Text.Read.Extra {-# DEPRECATED "Use Text.Read directly" #-} (
     module Text.Read,
     ) where
 
diff --git a/test/TestCustom.hs b/test/TestCustom.hs
--- a/test/TestCustom.hs
+++ b/test/TestCustom.hs
@@ -6,13 +6,13 @@
 import System.IO.Extra
 import Data.IORef
 import TestUtil
-import Data.Typeable.Extra as X
+import Data.List.Extra as X
 
 
 testCustom :: IO ()
 testCustom = do
     -- check that Extra really does export these things
-    testGen "Extra export" $ Proxy == (X.Proxy :: Proxy ())
+    testGen "Extra export" $ X.sort [1] == [1]
 
     testRaw "withTempFile" $ do
         xs <- replicateM 4 $ onceFork $ do
@@ -32,3 +32,12 @@
         ref <- newIORef 2
         retry 5 $ do modifyIORef ref pred; whenM ((/=) 0 <$> readIORef ref) $ fail "die"
         (==== 0) <$> readIORef ref
+
+    testRaw "barrier" $ do
+        bar <- newBarrier
+        (==== Nothing) <$> waitBarrierMaybe bar
+        signalBarrier bar 1
+        (==== Just 1) <$> waitBarrierMaybe bar
+        (==== 1) <$> waitBarrier bar
+        Left _ <- try_ $ signalBarrier bar 2
+        pure ()
diff --git a/test/TestGen.hs b/test/TestGen.hs
--- a/test/TestGen.hs
+++ b/test/TestGen.hs
@@ -241,6 +241,9 @@
     testGen "\\xs -> nubOrd xs == nub xs" $ \xs -> nubOrd xs == nub xs
     testGen "nubOrdOn length [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"test\",\"of\"]" $ nubOrdOn length ["a","test","of","this"] == ["a","test","of"]
     testGen "nubOrdBy (compare `on` length) [\"a\",\"test\",\"of\",\"this\"] == [\"a\",\"test\",\"of\"]" $ nubOrdBy (compare `on` length) ["a","test","of","this"] == ["a","test","of"]
+    testGen "zipWithLongest (,) \"a\" \"xyz\" == [(Just 'a', Just 'x'), (Nothing, Just 'y'), (Nothing, Just 'z')]" $ zipWithLongest (,) "a" "xyz" == [(Just 'a', Just 'x'), (Nothing, Just 'y'), (Nothing, Just 'z')]
+    testGen "zipWithLongest (,) \"a\" \"x\" == [(Just 'a', Just 'x')]" $ zipWithLongest (,) "a" "x" == [(Just 'a', Just 'x')]
+    testGen "zipWithLongest (,) \"\" \"x\" == [(Nothing, Just 'x')]" $ zipWithLongest (,) "" "x" == [(Nothing, Just 'x')]
     testGen "(1 :| [2,3]) |> 4 |> 5 == 1 :| [2,3,4,5]" $ (1 :| [2,3]) |> 4 |> 5 == 1 :| [2,3,4,5]
     testGen "[1,2,3] |: 4 |> 5 == 1 :| [2,3,4,5]" $ [1,2,3] |: 4 |> 5 == 1 :| [2,3,4,5]
     testGen "appendl (1 :| [2,3]) [4,5] == 1 :| [2,3,4,5]" $ appendl (1 :| [2,3]) [4,5] == 1 :| [2,3,4,5]
diff --git a/test/TestUtil.hs b/test/TestUtil.hs
--- a/test/TestUtil.hs
+++ b/test/TestUtil.hs
@@ -27,7 +27,6 @@
 import Data.Maybe as X
 import Data.Monoid as X
 import Data.Tuple.Extra as X
-import Data.Typeable.Extra as X
 import Data.Version.Extra as X
 import Numeric.Extra as X
 import System.Directory.Extra as X
