diff --git a/Data/List/Zipper.hs b/Data/List/Zipper.hs
new file mode 100644
--- /dev/null
+++ b/Data/List/Zipper.hs
@@ -0,0 +1,55 @@
+module Data.List.Zipper where
+
+data Zipper a = Zip ![a] ![a]
+
+empty :: Zipper a
+empty = Zip [] []
+
+fromList :: [a] -> Zipper a
+fromList as = Zip [] as
+
+fromListEnd :: [a] -> Zipper a
+fromListEnd as = Zip (reverse as) []
+
+toList :: Zipper a -> [a]
+toList (Zip ls rs) = reverse ls ++ rs
+
+beginp, endp, emptyp :: Zipper a -> Bool
+beginp (Zip [] _ ) = True
+beginp _           = False
+endp   (Zip _  []) = True
+endp   _           = False
+emptyp (Zip [] []) = True
+emptyp _           = False
+
+-- | @cursor z@ returns the targeted element in @z@.
+--
+-- This function is not total, but the invariant is that
+-- @endp z == False@ means that you can safely call
+-- @cursor z@.
+cursor :: Zipper a -> a
+cursor (Zip _ (a:_)) = a
+
+left, right :: Zipper a -> Zipper a
+left  (Zip ls (a:rs)) = Zip (a:ls) rs
+left  z               = z
+right (Zip (a:ls) rs) = Zip ls (a:rs)
+right z               = z
+
+insert, push :: a -> Zipper a -> Zipper a
+insert a (Zip ls rs) = Zip ls (a:rs)
+push   a (Zip ls rs) = Zip (a:ls) rs
+
+delete, pop :: Zipper a -> Zipper a
+delete (Zip ls (_:rs)) = Zip ls rs
+delete z               = z
+pop    (Zip (_:ls) rs) = Zip ls rs
+pop    z               = z
+
+-- | @replace a z@ changes the current element in the zipper
+-- to the passed in value.  If there is no current element,
+-- the zipper is unchanged.  If you want to add the element
+-- in that case instead, use @insert a (delete z)@.
+replace :: a -> Zipper a -> Zipper a
+replace a (Zip ls (_:rs)) = Zip ls (a:rs)
+replace _ z               = z
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2008, Ryan Ingram
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+   this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/ListZipper.cabal b/ListZipper.cabal
new file mode 100644
--- /dev/null
+++ b/ListZipper.cabal
@@ -0,0 +1,18 @@
+name:            ListZipper
+version:         1.0.0.0
+copyright:       (c) 2008 Ryan Ingram
+license:         BSD3
+license-file:    LICENSE
+author:          Ryan Ingram <ryani.spam@gmail.com>
+maintainer:      Ryan Ingram <ryani.spam@gmail.com>
+category:        Data
+synopsis:        Simple zipper for lists
+description:     List zipper with O(1) get element at cursor, insert at cursor,
+                 delete at cursor, move right, and move left operations.
+stability:       stable
+build-type:      Simple
+cabal-version:   >= 1.2.1
+
+library
+  exposed-modules: Data.List.Zipper
+  build-depends:   base >= 3.0
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
