diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 Donnacha Oisín Kidney
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,73 @@
+[![Build Status](https://travis-ci.org/oisdk/groupBy.svg?branch=master)](https://travis-ci.org/oisdk/groupBy)
+
+# groupBy
+
+This provides a drop-in replacement for [`Data.List.groupBy`](https://hackage.haskell.org/package/base-4.10.1.0/docs/Data-List.html#v:groupBy), with benchmarks and tests.
+
+The original `Data.List.groupBy` has (perhaps unexpected) behaviour, in that it compares elements to the first in the group, not adjacent ones. In other words, if you wanted to group into ascending sequences:
+
+```haskell
+>>> Data.List.groupBy (<=) [1,2,2,3,1,2,0,4,5,2]
+[[1,2,2,3,1,2],[0,4,5,2]]
+```
+
+The replacement has three distinct advantages:
+
+1. It groups adjacent elements, allowing the example above to function as expected:
+
+   ```haskell
+   >>> Data.List.GroupBy.groupBy (<=) [1,2,2,3,1,2,0,4,5,2]
+   [[1,2,2,3],[1,2],[0,4,5],[2]]
+   ```
+
+2. It is a good producer and consumer, with rules similar to those for [`Data.List.scanl`](https://hackage.haskell.org/package/base-4.10.1.0/docs/src/GHC.List.html#scanl). The old version was defined in terms of [`span`](https://hackage.haskell.org/package/base-4.10.1.0/docs/Data-List.html#v:span):
+
+   ```haskell
+   groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
+   groupBy _  []           =  []
+   groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
+                              where (ys,zs) = span (eq x) xs
+   ```
+   
+   Which prevents it from being a good producer/consumer.
+
+3. It is significantly faster than the original in most cases.
+   
+## Tests
+
+Tests ensure that the function is the same as the original when the relation supplied is an equivalence, and that it performs the expected adjacent comparisons when the relation isn't transitive.
+
+The tests also check that laziness is maintained, as defined by:
+
+```haskell
+>>> head (groupBy (==) (1:2:undefined))
+[1]
+
+>>> (head . head) (groupBy undefined (1:undefined))
+1
+
+>>> (head . head . tail) (groupBy (==) (1:2:undefined))
+2
+```
+
+## Benchmarks
+
+Benchmarks compare the function to three other implementations: the current [`Data.List.groupBy`](https://hackage.haskell.org/package/base-4.10.1.0/docs/src/Data.OldList.html#groupBy), a [version](https://hackage.haskell.org/package/utility-ht-0.0.14/docs/Data-List-HT.html#v:groupBy) provided by the [utility-ht](https://hackage.haskell.org/package/utility-ht) package, and [a version provided by Brandon Simmons](http://brandon.si/code/an-alternative-definition-for-datalistgroupby/).
+
+The benchmarks test functions that force the outer list:
+
+```haskell
+length . groupBy eq
+```
+
+And functions which force the contents of the inner lists:
+
+```haskell
+sum' = foldl' (+) 0
+
+sum' . map sum' . groupBy eq
+```
+
+Each benchmark is run on lists where the groups are small, the groups are large, and where there is only one group. The default size is 10000, but other sizes can be provided with the `--size=[x,y,z]` flag to the benchmarks.
+
+The new definition is slower than the old only when the size of the sublists is much larger than the size of the outer list. To make the newer definition faster in *that* case, you would simply force the pair (or use a strict pair) from the accumulator. However, this makes the new definition match the old speed in the other cases, which I would imagine are more common.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/bench/Data/List/GroupBy/Alternative.hs b/bench/Data/List/GroupBy/Alternative.hs
new file mode 100644
--- /dev/null
+++ b/bench/Data/List/GroupBy/Alternative.hs
@@ -0,0 +1,16 @@
+-- | This module provides an alternative definition of
+-- 'Data.List.groupBy', written by
+-- <http://brandon.si/code/an-alternative-definition-for-datalistgroupby/ Brandon Simmons>.
+module Data.List.GroupBy.Alternative where
+
+groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
+groupBy _ [] = []
+groupBy c (a:as) = (a : ys) : groupBy c zs
+  where
+    (ys,zs) = spanC a as
+    spanC _ [] = ([], [])
+    spanC a' (x:xs)
+      | a' `c` x =
+          let (ps,qs) = spanC x xs
+          in (x : ps, qs)
+      | otherwise = ([], x : xs)
diff --git a/bench/bench.hs b/bench/bench.hs
new file mode 100644
--- /dev/null
+++ b/bench/bench.hs
@@ -0,0 +1,132 @@
+module Main (main) where
+
+import           Criterion.Main
+import           Criterion.Main.Options
+import           Options.Applicative
+import           System.IO.CodePage            (withCP65001)
+
+import           System.Random
+
+import qualified Data.List                     as List
+import qualified Data.List.GroupBy             as GroupBy
+import qualified Data.List.GroupBy.Alternative as Alt
+import qualified Data.List.HT                  as HT
+
+import           Control.Monad
+import           Data.Foldable
+import           Data.Semigroup                ((<>))
+
+{-# ANN module "HLint: ignore Use group" #-}
+
+smallInt :: IO Int
+smallInt = randomRIO (-3,3)
+
+twoInt :: IO Int
+twoInt = randomRIO (0,1)
+
+sum' :: [Int] -> Int
+sum' = foldl' (+) 0
+{-# INLINE sum' #-}
+
+outerLengthSmallGroups :: Int -> Benchmark
+outerLengthSmallGroups n =
+    env (replicateM n smallInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (length . List.groupBy    (==)) xs
+             , bench "GroupBy"   $ nf (length . GroupBy.groupBy (==)) xs
+             , bench "Alt"       $ nf (length . Alt.groupBy     (==)) xs
+             , bench "HT"        $ nf (length . HT.groupBy      (==)) xs]
+
+outerLengthLargeGroups :: Int -> Benchmark
+outerLengthLargeGroups n =
+    env (replicateM n twoInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (length . List.groupBy    (==)) xs
+             , bench "GroupBy"   $ nf (length . GroupBy.groupBy (==)) xs
+             , bench "Alt"       $ nf (length . Alt.groupBy     (==)) xs
+             , bench "HT"        $ nf (length . HT.groupBy      (==)) xs]
+
+
+outerLengthOneGroup :: Int -> Benchmark
+outerLengthOneGroup n =
+    env (replicateM n smallInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (length . List.groupBy    (\_ _ -> True)) xs
+             , bench "GroupBy"   $ nf (length . GroupBy.groupBy (\_ _ -> True)) xs
+             , bench "Alt"       $ nf (length . Alt.groupBy     (\_ _ -> True)) xs
+             , bench "HT"        $ nf (length . HT.groupBy      (\_ _ -> True)) xs]
+
+sumSmallGroups :: Int -> Benchmark
+sumSmallGroups n =
+    env (replicateM n smallInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (sum' . map sum' . List.groupBy    (==)) xs
+             , bench "GroupBy"   $ nf (sum' . map sum' . GroupBy.groupBy (==)) xs
+             , bench "Alt"       $ nf (sum' . map sum' . Alt.groupBy     (==)) xs
+             , bench "HT"        $ nf (sum' . map sum' . HT.groupBy      (==)) xs]
+
+sumLargeGroups :: Int -> Benchmark
+sumLargeGroups n =
+    env (replicateM n twoInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (sum' . map sum' . List.groupBy    (==)) xs
+             , bench "GroupBy"   $ nf (sum' . map sum' . GroupBy.groupBy (==)) xs
+             , bench "Alt"       $ nf (sum' . map sum' . Alt.groupBy     (==)) xs
+             , bench "HT"        $ nf (sum' . map sum' . HT.groupBy      (==)) xs]
+
+sumOneGroup :: Int -> Benchmark
+sumOneGroup n =
+    env (replicateM n smallInt) $
+    \xs ->
+         bgroup
+             (show n)
+             [ bench "Data.List" $ nf (sum' . map sum' . List.groupBy    (\_ _ -> True)) xs
+             , bench "GroupBy"   $ nf (sum' . map sum' . GroupBy.groupBy (\_ _ -> True)) xs
+             , bench "Alt"       $ nf (sum' . map sum' . Alt.groupBy     (\_ _ -> True)) xs
+             , bench "HT"        $ nf (sum' . map sum' . HT.groupBy      (\_ _ -> True)) xs]
+
+defaultSizes :: [Int]
+defaultSizes = [10000]
+
+sizesParser :: Parser [Int]
+sizesParser =
+    option
+        auto
+        (long "sizes" <> help "a list of the sizes on which to run groupBy" <>
+         value defaultSizes)
+
+overParser :: (Parser a -> Parser b) -> ParserInfo a -> ParserInfo b
+overParser f p =
+    p
+    { infoParser = f (infoParser p)
+    }
+
+
+main :: IO ()
+main =
+    withCP65001 $
+    do (sizes,wat) <-
+           execParser
+               (overParser (liftA2 (,) sizesParser) (describe defaultConfig))
+       runMode
+           wat
+           [ bgroup
+                 "length"
+                 [ bgroup "small" (map outerLengthSmallGroups sizes)
+                 , bgroup "large" (map outerLengthLargeGroups sizes)
+                 , bgroup "one" (map outerLengthOneGroup sizes)]
+           , bgroup
+                 "sum"
+                 [ bgroup "small" (map sumSmallGroups sizes)
+                 , bgroup "large" (map sumLargeGroups sizes)
+                 , bgroup "one" (map sumOneGroup sizes)]]
diff --git a/groupBy.cabal b/groupBy.cabal
new file mode 100644
--- /dev/null
+++ b/groupBy.cabal
@@ -0,0 +1,71 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 5ed955d1b08c17d3fc7493e3d203926196cab317f3563179a75efd4203ade51e
+
+name:           groupBy
+version:        0.1.0.0
+synopsis:       Replacement definition of Data.List.GroupBy
+description:    Please see the README on Github at <https://github.com/oisdk/groupBy#readme>
+category:       Data
+homepage:       https://github.com/oisdk/groupBy#readme
+bug-reports:    https://github.com/oisdk/groupBy/issues
+author:         Donnacha Oisín Kidney
+maintainer:     mail@doisinkidney.com
+copyright:      2018 Donnacha Oisín Kidney
+license:        MIT
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+
+extra-source-files:
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/oisdk/groupBy
+
+library
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4 && <5
+  exposed-modules:
+      Data.List.GroupBy
+  other-modules:
+      Paths_groupBy
+  default-language: Haskell2010
+
+test-suite groupBy-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      QuickCheck
+    , base >=4 && <5
+    , doctest
+    , groupBy
+  other-modules:
+      Paths_groupBy
+  default-language: Haskell2010
+
+benchmark bench
+  type: exitcode-stdio-1.0
+  main-is: bench.hs
+  hs-source-dirs:
+      bench
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2
+  build-depends:
+      base >=4 && <5
+    , code-page
+    , criterion
+    , groupBy
+    , optparse-applicative
+    , random
+    , utility-ht
+  other-modules:
+      Data.List.GroupBy.Alternative
+  default-language: Haskell2010
diff --git a/src/Data/List/GroupBy.hs b/src/Data/List/GroupBy.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/List/GroupBy.hs
@@ -0,0 +1,123 @@
+{-# LANGUAGE CPP #-}
+{-# LANGUAGE Trustworthy #-}
+-- |
+-- Module      : Data.List.GroupBy
+-- Description : A replacement for 'Data.List.groupBy'
+-- Copyright   : (c) Donnacha Oisín Kidney, 2018
+-- License     : MIT
+-- Maintainer  : mail@doisinkidney.com
+-- Stability   : experimental
+-- Portability : portable
+--
+-- This module provides an alternative definition for
+-- 'Data.List.groupBy' which does not require a transitive
+-- equivalence predicate.
+module Data.List.GroupBy
+  (groupBy
+  ,group)
+  where
+
+#if __GLASGOW_HASKELL__
+import           GHC.Base (build, foldr
+#if __GLASGOW_HASKELL__ >= 800
+                           ,oneShot
+#endif
+                          )
+import           Prelude  hiding (foldr)
+#endif
+
+-- | Groups adjacent elements according to some relation.
+-- The relation can be an equivalence:
+--
+--
+-- >>> groupBy (==) "aaabcccdda"
+-- ["aaa","b","ccc","dd","a"]
+--
+-- >>> groupBy (==) []
+-- []
+--
+-- However, it need not be. The function compares adjacent
+-- elements only, so it can be used to find increasing
+-- subsequences:
+--
+-- >>> groupBy (<=) [1,2,2,3,1,2,0,4,5,2]
+-- [[1,2,2,3],[1,2],[0,4,5],[2]]
+--
+-- It is fully lazy:
+--
+-- >>> head (groupBy (==) (1:2:undefined))
+-- [1]
+--
+-- >>> (head . head) (groupBy undefined (1:undefined))
+-- 1
+--
+-- >>> (head . head . tail) (groupBy (==) (1:2:undefined))
+-- 2
+--
+-- prop> xs === concat (groupBy (applyFun2 p) xs)
+-- prop> all (not . null) (groupBy (applyFun2 p) xs)
+
+groupBy :: (a -> a -> Bool) -> [a] -> [[a]]
+groupBy _ [] = []
+groupBy p' (x':xs') = (x' : ys') : zs'
+  where
+    (ys',zs') = go p' x' xs'
+    go p z (x:xs)
+      | p z x = (x : ys, zs)
+      | otherwise = ([], (x : ys) : zs)
+      where (ys,zs) = go p x xs
+    go _ _ [] = ([], [])
+
+#if __GLASGOW_HASKELL__
+{-# NOINLINE [1] groupBy #-}
+{-# INLINE [0] groupByFB #-}
+{-# ANN groupByFB "hlint: ignore Redundant lambda" #-}
+groupByFB
+    :: (a -> a -> Bool)
+    -> ([a] -> b -> b)
+    -> a
+    -> ((a -> Bool) -> ([a], b))
+    -> (a -> Bool)
+    -> ([a], b)
+groupByFB p c =
+    \x a ->
+#if __GLASGOW_HASKELL__ >= 800
+         oneShot
+#endif
+             (\q ->
+                   let (ys,zs) = a (p x)
+                   in if q x
+                          then (x : ys, zs)
+                          else ([], c (x : ys) zs))
+
+
+{-# INLINE [0] constGroupBy #-}
+constGroupBy :: a -> b -> ([c], a)
+constGroupBy n = const ([], n)
+
+{-# INLINE [0] constFalse #-}
+constFalse :: a -> Bool
+constFalse = const False
+
+{-# INLINE [0] sndGroupBy #-}
+sndGroupBy :: (a, b) -> b
+sndGroupBy = snd
+{-# RULES
+"groupBy"     [~1] forall p xs. groupBy p xs = build (\c n -> sndGroupBy (foldr (groupByFB p c) (constGroupBy n) xs constFalse))
+"groupByList" [1]  forall p xs. sndGroupBy (foldr (groupByFB p (:)) (constGroupBy []) xs constFalse) = groupBy p xs #-}
+
+#else
+{-# INLINE groupBy #-}
+#endif
+
+-- | Groups adjacent equal elements.
+--
+-- >>> group "aaabcccdda"
+-- ["aaa","b","ccc","dd","a"]
+group :: Eq a => [a] -> [[a]]
+group = groupBy (==)
+{-# INLINE group #-}
+
+-- $setup
+-- >>> import Test.QuickCheck
+-- >>> import Test.QuickCheck.Function
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,24 @@
+import Test.DocTest
+import Test.QuickCheck
+import Test.QuickCheck.Poly
+
+import Data.List.GroupBy
+
+sameGroupEqual :: Blind (A -> A -> Bool) -> [A] -> Bool
+sameGroupEqual (Blind p) =
+    all
+        (\ys ->
+              and (zipWith p ys (tail ys))) .
+    groupBy p
+
+differentGroupUnequal :: Blind (A -> A -> Bool) -> [A] -> Bool
+differentGroupUnequal (Blind p) xs = and (zipWith t groups (tail groups))
+  where
+    groups = groupBy p xs
+    t ys zs = not (p (last ys) (head zs))
+
+main :: IO ()
+main = do
+    quickCheck sameGroupEqual
+    quickCheck differentGroupUnequal
+    doctest ["-isrc", "src/"]
