diff --git a/either-list-functions.cabal b/either-list-functions.cabal
new file mode 100644
--- /dev/null
+++ b/either-list-functions.cabal
@@ -0,0 +1,45 @@
+-- This file has been generated from package.yaml by hpack version 0.18.1.
+--
+-- see: https://github.com/sol/hpack
+
+name:           either-list-functions
+version:        0.0.0.1
+synopsis:       Functions involving lists of Either
+description:    Functions involving lists of Either
+category:       Data
+homepage:       https://github.com/chris-martin/either-list-functions#readme
+bug-reports:    https://github.com/chris-martin/either-list-functions/issues
+author:         Chris Martin <ch.martin@gmail.com>
+maintainer:     Chris Martin <ch.martin@gmail.com>
+license:        Apache-2.0
+license-file:   license.txt
+build-type:     Simple
+cabal-version:  >= 1.10
+
+source-repository head
+  type: git
+  location: https://github.com/chris-martin/either-list-functions
+
+library
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      base >= 4.9 && < 4.10
+  exposed-modules:
+      Data.List.EitherFunctions
+  other-modules:
+      Paths_either_list_functions
+  default-language: Haskell2010
+
+test-suite doctest
+  type: exitcode-stdio-1.0
+  main-is: doctest.hs
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded
+  build-depends:
+      base >= 4.9 && < 4.10
+    , either-list-functions
+    , doctest
+  default-language: Haskell2010
diff --git a/license.txt b/license.txt
new file mode 100644
--- /dev/null
+++ b/license.txt
@@ -0,0 +1,13 @@
+Copyright 2017 Chris Martin
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/src/Data/List/EitherFunctions.hs b/src/Data/List/EitherFunctions.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/List/EitherFunctions.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE LambdaCase        #-}
+{-# LANGUAGE NoImplicitPrelude #-}
+
+module Data.List.EitherFunctions
+  ( partlyMap
+  , groupEither
+  , spanLeft
+  , spanRight
+  ) where
+
+import Data.Either (Either (..))
+import Data.List   (map)
+import Data.Maybe  (Maybe (..), maybe)
+
+{- |
+
+>>> import Prelude (even, show)
+
+>>> partlyMap (\x -> if even x then Just (show x) else Nothing) [1..5]
+[Left 1,Right "2",Left 3,Right "4",Left 5]
+
+-}
+partlyMap :: (a -> Maybe b) -> [a] -> [Either a b]
+partlyMap f = map (\x -> maybe (Left x) Right (f x))
+
+{- |
+
+>>> groupEither [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']
+[Left [1,2],Right "a",Left [3],Right "bc"]
+
+-}
+groupEither :: [Either a b] -> [Either [a] [b]]
+groupEither [] = []
+groupEither ((Left x) : xs)  = let (ys, zs) = spanLeft xs
+                               in  Left (x : ys) : groupEither zs
+groupEither ((Right x) : xs) = let (ys, zs) = spanRight xs
+                               in  Right (x : ys) : groupEither zs
+
+{- |
+
+>>> spanLeft [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']
+([1,2],[Right 'a',Left 3,Right 'b',Right 'c'])
+
+>>> spanLeft [Right 'a', Left 3, Right 'b', Right 'c']
+([],[Right 'a',Left 3,Right 'b',Right 'c'])
+
+-}
+spanLeft :: [Either a b] -> ([a], [Either a b])
+spanLeft [] = ([], [])
+spanLeft ((Left x) : xs) = let (ys, zs) = spanLeft xs
+                           in  (x : ys, zs)
+spanLeft xs = ([], xs)
+
+{- |
+
+>>> spanRight [Left 1, Left 2, Right 'a', Left 3, Right 'b', Right 'c']
+("",[Left 1,Left 2,Right 'a',Left 3,Right 'b',Right 'c'])
+
+>>> spanRight [Right 'a', Left 3, Right 'b', Right 'c']
+("a",[Left 3,Right 'b',Right 'c'])
+
+-}
+spanRight :: [Either a b] -> ([b], [Either a b])
+spanRight [] = ([], [])
+spanRight ((Right x) : xs) = let (ys, zs) = spanRight xs
+                             in  (x : ys, zs)
+spanRight xs = ([], xs)
diff --git a/test/doctest.hs b/test/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest.hs
@@ -0,0 +1,4 @@
+import Test.DocTest
+
+main :: IO ()
+main = doctest ["src"]
