diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Kwang Yul Seo (c) 2016
+
+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 Kwang Yul Seo nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+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/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/src/Data/Text/Zipper/Edit.hs b/src/Data/Text/Zipper/Edit.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Text/Zipper/Edit.hs
@@ -0,0 +1,179 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+
+{-|
+Module      :  Data.Text.TextZipper.Edit
+Copyright   :  (c) Kwang Yul Seo 2016
+License     :  BSD-style (see the file LICENSE)
+
+Maintainer  :  kwangyul.seo@gmail.com
+Stability   :  experimental
+Portability :  portable
+
+This module provides a monadic interface to 'Data.Text.Zipper.TextZipper'.
+-}
+module Data.Text.Zipper.Edit
+  ( Edit
+  , EditT
+  , execEditT
+  , execEdit
+  -- * Extraction functions
+  , clearZipper
+  , getText
+  , currentLine
+  , cursorPosition
+  , lineLengths
+  , getLineLimit
+  -- * Navigation and editing functions
+  , moveCursor
+  , insertChar
+  , breakLine
+  , killToEOL
+  , gotoEOL
+  , gotoBOL
+  , deletePrevChar
+  , deleteChar
+  , moveRight
+  , moveLeft
+  , moveUp
+  , moveDown
+  ) where
+
+import Control.Monad.Identity
+import Control.Monad.State
+import Data.Text.Zipper (TextZipper)
+import qualified Data.Text.Zipper as TZ
+
+-- | An edit monad
+type Edit t a = EditT t Identity a
+
+-- | An edit transformer monad
+newtype EditT t m a = EditT { unEdit :: StateT (TextZipper t) m a }
+  deriving (Functor, Applicative, Monad, MonadState (TextZipper t))
+
+-- | Execute the edit session with the given zipper and return the
+-- modified zipper.
+execEditT :: (Monoid t, Monad m) => EditT t m a -> TextZipper t -> m (TextZipper t)
+execEditT e tz = execStateT (unEdit e) tz
+
+-- | Execute the edit session with the given zipper and return the
+-- modified zipper.
+execEdit :: (Monoid t) => Edit t a -> TextZipper t -> TextZipper t
+execEdit e tz = runIdentity (execEditT e tz)
+
+-- | Empty a zipper.
+clearZipper :: (Monoid t, Monad m) => EditT t m ()
+clearZipper = do
+  z <- get
+  put (TZ.clearZipper z)
+
+-- | Get the text contents of the zipper.
+getText :: (Monoid t, Monad m) => EditT t m [t]
+getText = get >>= return . TZ.getText
+
+-- | The line of text on which the zipper's cursor currently resides.
+currentLine :: (Monoid t, Monad m) => EditT t m t
+currentLine = get >>= return . TZ.currentLine
+
+-- | Get the cursor position of the zipper; returns @(row, col)@.
+-- @row@ ranges from @[0..num_rows-1]@ inclusive; @col@ ranges from
+-- @[0..length of current line]@ inclusive.  Column values equal to
+-- line width indicate a cursor that is just past the end of a line of
+-- text.
+cursorPosition :: (Monoid t, Monad m) => EditT t m (Int, Int)
+cursorPosition = get >>= return . TZ.cursorPosition
+
+-- | Return the lengths of the lines in the zipper.
+lineLengths :: (Monoid t, Monad m) => EditT t m [Int]
+lineLengths = get >>= return . TZ.lineLengths
+
+-- | Get the line limit, if any, for a zipper.
+getLineLimit :: (Monoid t, Monad m) => EditT t m (Maybe Int)
+getLineLimit = get >>= return . TZ.getLineLimit
+
+-- | Move the cursor to the specified row and column.  Invalid cursor
+-- positions will be ignored.  Valid cursor positions range as
+-- described for 'cursorPosition'.
+moveCursor ::  (Monoid t, Monad m) => (Int, Int) -> EditT t m ()
+moveCursor c = do
+  z <- get
+  put (TZ.moveCursor c z)
+
+-- | Insert a character at the current cursor position.  Move the
+-- cursor one position to the right.
+insertChar :: (Monoid t, Monad m) => Char -> EditT t m ()
+insertChar ch = do
+  z <- get
+  put (TZ.insertChar ch z)
+
+-- |Insert a line break at the current cursor position.
+breakLine :: (Monoid t, Monad m) => EditT t m ()
+breakLine = do
+  z <- get
+  put (TZ.breakLine z)
+
+-- | Remove all text from the cursor position to the end of the current
+-- line.  If the cursor is at the beginning of a line and the line is
+-- empty, the entire line will be removed.
+killToEOL :: (Monoid t, Monad m) => EditT t m ()
+killToEOL = do
+  z <- get
+  put (TZ.killToEOL z)
+
+-- | Move the cursor to the end of the current line.
+gotoEOL :: (Monoid t, Monad m) => EditT t m ()
+gotoEOL = do
+  z <- get
+  put (TZ.gotoEOL z)
+
+-- | Move the cursor to the beginning of the current line.
+gotoBOL :: (Monoid t, Monad m) => EditT t m ()
+gotoBOL = do
+  z <- get
+  put (TZ.gotoBOL z)
+
+-- | Delete the character preceding the cursor position, and move the
+-- cursor backwards by one character.
+deletePrevChar :: (Eq t, Monoid t, Monad m) => EditT t m ()
+deletePrevChar = do
+  z <- get
+  put (TZ.deletePrevChar z)
+
+-- | Delete the character at the cursor position.  Leaves the cursor
+-- position unchanged.  If the cursor is at the end of a line of text,
+-- this combines the line with the line below.
+deleteChar :: (Monoid t, Monad m) => EditT t m ()
+deleteChar = do
+  z <- get
+  put (TZ.deleteChar z)
+
+-- | Move the cursor right by one position.  If the cursor is at the
+-- end of a line, the cursor is moved to the first position of the
+-- following line (if any).
+moveRight :: (Monoid t, Monad m) => EditT t m ()
+moveRight = do
+  z <- get
+  put (TZ.moveRight z)
+
+-- | Move the cursor left by one position.  If the cursor is at the
+-- beginning of a line, the cursor is moved to the last position of
+-- the preceding line (if any).
+moveLeft :: (Monoid t, Monad m) => EditT t m ()
+moveLeft = do
+  z <- get
+  put (TZ.moveLeft z)
+
+-- | Move the cursor up by one row.  If there no are rows above the
+-- current one, move to the first position of the current row.  If the
+-- row above is shorter, move to the end of that row.
+moveUp :: (Monoid t, Monad m) => EditT t m ()
+moveUp = do
+  z <- get
+  put (TZ.moveUp z)
+
+-- | Move the cursor down by one row.  If there are no rows below the
+-- current one, move to the last position of the current row.  If the
+-- row below is shorter, move to the end of that row.
+moveDown :: (Monoid t, Monad m) => EditT t m ()
+moveDown = do
+  z <- get
+  put (TZ.moveDown z)
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,17 @@
+import Test.Hspec
+
+import Data.Text.Zipper
+import qualified Data.Text.Zipper.Edit as Z
+
+tz = stringZipper ["abc", "def"] Nothing
+
+insertXAtTheBeginning = do
+  Z.moveCursor (0, 0)
+  Z.insertChar 'x'
+
+main :: IO ()
+main = hspec $ do
+  describe "execEdit" $ do
+    it "execute the edit session with the given zipper and return the modified zipper" $ do
+      let newTz = Z.execEdit insertXAtTheBeginning tz
+      getText newTz `shouldBe` ["xabc", "def"]
diff --git a/text-zipper-monad.cabal b/text-zipper-monad.cabal
new file mode 100644
--- /dev/null
+++ b/text-zipper-monad.cabal
@@ -0,0 +1,37 @@
+name:                text-zipper-monad
+version:             0.1.0.0
+synopsis:            Monadic interface to the text-zipper package
+description:         Please see README.md
+homepage:            https://github.com/kseo/text-zipper-monad#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Kwang Yul Seo
+maintainer:          kwangyul.seo@gmail.com
+copyright:           BSD3
+category:            Text
+build-type:          Simple
+-- extra-source-files:
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Data.Text.Zipper.Edit
+  build-depends:       base >= 4.7 && < 5
+                     , mtl >= 2.2 && < 2.3
+                     , text-zipper >= 0.4 && < 0.5
+  default-language:    Haskell2010
+
+test-suite text-zipper-monad-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , hspec
+                     , text-zipper
+                     , text-zipper-monad
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/kseo/text-zipper-monad
