diff --git a/Data/List/PointedList.hs b/Data/List/PointedList.hs
new file mode 100644
--- /dev/null
+++ b/Data/List/PointedList.hs
@@ -0,0 +1,145 @@
+-- | An implementation of a zipper-like non-empty list structure that tracks
+--   an index position in the list (the 'focus').
+module Data.List.PointedList where
+
+import Prelude hiding (foldl, foldr)
+
+import Control.Applicative
+import Control.Monad
+import Data.Foldable
+import Data.List hiding (foldl, foldr)
+import Data.Maybe
+import Data.Traversable
+
+-- | The implementation of the pointed list structure which tracks the current
+--   position in the list structure.
+data PointedList a = PointedList [a] a [a]
+ deriving (Eq, Ord)
+
+instance (Show a) => Show (PointedList a) where
+ show (PointedList ls x rs) = show (reverse ls) ++ " " ++ show x ++ " " ++ show rs
+
+instance Functor PointedList where
+ fmap f (PointedList ls x rs) = PointedList (map f ls) (f x) (map f rs)
+
+instance Foldable PointedList where
+ foldr f z (PointedList ls x rs) = foldl (flip f) (foldr f z (x:rs)) ls
+
+instance Traversable PointedList where
+ traverse f (PointedList ls x rs) = PointedList <$> (reverse <$> traverse f (reverse ls)) <*> f x <*> traverse f rs
+
+-- | Create a @PointedList@ with a single element.
+singleton :: a -> PointedList a
+singleton x = PointedList [] x []
+
+-- | Possibly create a @Just PointedList@ if the provided list has at least one
+--   element; otherwise, return Nothing.
+--
+--   The provided list's head will be the focus of the list, and the rest of
+--   list will follow on the right side.
+fromList :: [a] -> Maybe (PointedList a)
+fromList []     = Nothing
+fromList (x:xs) = Just $ PointedList [] x xs
+
+-- | Possibly create a @Just PointedList@ if the provided list has at least one
+--   element; otherwise, return Nothing.
+--
+--   The provided list's last element will be the focus of the list, following
+--   the rest of the list in order, to the left.
+fromListEnd :: [a] -> Maybe (PointedList a)
+fromListEnd [] = Nothing
+fromListEnd xs = Just $ PointedList xs' x []
+ where (x:xs') = reverse xs
+
+-- | The focus element of the pointed list.
+focus :: PointedList a -> a
+focus (PointedList _ x _) = x
+
+-- | Possibly move the focus to the next element in the list.
+next :: PointedList a -> Maybe (PointedList a)
+next (PointedList _  _ []) = Nothing
+next p = (Just . tryNext) p -- GHC doesn't allow PL form here
+
+-- | Attempt to move the focus to the next element, or 'error' if there are
+--   no more elements.
+tryNext :: PointedList a -> PointedList a
+tryNext p@(PointedList _  _ []    ) = error "cannot move to next element"
+tryNext   (PointedList ls x (r:rs)) = PointedList (x:ls) r rs
+
+-- | Possibly move the focus to the previous element in the list.
+previous :: PointedList a -> Maybe (PointedList a)
+previous (PointedList [] _ _ ) = Nothing
+previous p = (Just . tryPrevious) p
+
+-- | Attempt to move the focus to the previous element, or 'error' if there are
+--   no more elements.
+tryPrevious :: PointedList a -> PointedList a
+tryPrevious p@(PointedList []     _ _ ) =
+  error "cannot move to previous element" 
+tryPrevious   (PointedList (l:ls) x rs) = PointedList ls l (x:rs)
+
+-- | An alias for 'deleteRight'.
+insert :: a -> PointedList a -> PointedList a
+insert = insertRight
+
+-- | Insert an element to the left of the focus, then move the focus to the new
+--   element.
+insertLeft :: a -> PointedList a -> PointedList a
+insertLeft y (PointedList ls x rs) = PointedList ls y (x:rs)
+
+-- | Insert an element to the right of the focus, then move the focus to the
+--   new element.
+insertRight :: a -> PointedList a -> PointedList a
+insertRight y (PointedList ls x rs) = PointedList (x:ls) y rs
+
+-- | An alias of 'deleteRight'.
+delete :: PointedList a -> Maybe (PointedList a)
+delete = deleteRight
+
+-- | Possibly delete the element at the focus, then move the element on the
+--   left to the focus. If no element is on the left, focus on the element to
+--   the right. If the deletion will cause the list to be empty, return
+--   @Nothing@.
+deleteLeft :: PointedList a -> Maybe (PointedList a)
+deleteLeft (PointedList [] _ []    ) = Nothing
+deleteLeft (PointedList (l:ls) _ rs) = Just $ PointedList ls l rs
+deleteLeft (PointedList [] _ (r:rs)) = Just $ PointedList [] r rs
+
+-- | Possibly delete the element at the focus, then move the element on the
+--   right to the focus. If no element is on the right, focus on the element to
+--   the left. If the deletion will cause the list to be empty, return
+--   @Nothing@.
+deleteRight :: PointedList a -> Maybe (PointedList a)
+deleteRight (PointedList [] _ []    ) = Nothing
+deleteRight (PointedList ls _ (r:rs)) = Just $ PointedList ls r rs
+deleteRight (PointedList (l:ls) _ []) = Just $ PointedList ls l []
+
+-- | The length of the list.
+length :: PointedList a -> Int
+length = foldr (const (+1)) 0
+
+-- | Whether the focus is the first element.
+atStart :: PointedList a -> Bool
+atStart (PointedList [] _ _) = True
+atStart _ = False
+
+-- | Whether the focus is the last element.
+atEnd :: PointedList a -> Bool
+atEnd (PointedList _ _ []) = True
+atEnd _ = False
+
+-- | Create a @PointedList@ of variations of the provided @PointedList@, in
+--   which each element is focused, with the provided @PointedList@ as the
+--   focus of the sets.
+positions :: PointedList a -> PointedList (PointedList a)
+positions p@(PointedList ls x rs) = PointedList left p right
+ where left  = unfoldr (\p -> fmap (join (,)) $ previous p) p
+       right = unfoldr (\p -> fmap (join (,)) $ next p) p
+
+-- | Map over the @PointedList@s created via 'positions', such that @f@ is
+--   called with each element of the list focused in the provided
+--   @PointedList@. An example makes this easier to understand:
+--
+-- > contextMap atStart (fromJust $ fromList [1..5])
+contextMap :: (PointedList a -> b) -> PointedList a -> PointedList b
+contextMap f z = fmap f $ positions z
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+* Copyright (c) 2009, Jeff Wheeler
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are met:
+*     * Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*     * 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.
+*     * Neither the name of the <organization> nor the
+*       names of its contributors may be used to endorse or promote products
+*       derived from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY <copyright holder> ''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 <copyright holder> 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/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,5 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+
+main :: IO ()
+main = defaultMain
diff --git a/pointedlist.cabal b/pointedlist.cabal
new file mode 100644
--- /dev/null
+++ b/pointedlist.cabal
@@ -0,0 +1,15 @@
+Name:          pointedlist
+Version:       0.0.1
+Synopsis:      A zipper-like comonad which works as a list, tracking a position.
+Category:      Data
+Description:
+    A PointedList tracks the position in a non-empty list which works similarly
+    to a zipper. A current item is always required, and therefore the list may
+    never be empty.
+License:       BSD3
+License-file:  LICENSE
+Author:        Jeff Wheeler
+Maintainer:    jeffwheeler@gmail.com
+Build-type:    Simple
+Build-depends: base>=4
+Exposed-modules: Data.List.PointedList
