diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,4 @@
+# 0.1.1.1
+
+- Initial release.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Koji Miyazato (c) 2018
+
+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 Koji Miyazato 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/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+## matchable
+
+This package provides a type class `Matchable`, which represents
+`zipMatch` operation which can zip two values.
+
+`zipMatch` operation can fail, and it returns zipped value wrapped
+in `Maybe`. Specifically, `zipMatch` returns zipped value if and only if two arguments
+have exactly same shape.
+
+### Example
+
+``` haskell
+>>> zipMatch [1,2] ['a','b']
+Just [(1,'a'), (2,'b')]
+>>> zipMatch [1,2,3] ['a','b']
+Nothing
+```
+
+See [example](https://github.com/viercc/matchable/blob/master/example/README.md) also.
diff --git a/matchable.cabal b/matchable.cabal
new file mode 100644
--- /dev/null
+++ b/matchable.cabal
@@ -0,0 +1,54 @@
+name:                matchable
+version:             0.1.1.1
+synopsis:            A type class for Matchable Functors.
+description:         This package provides a type class @Matchable@, which represents
+                     @zipMatch@ operation which can zip two values if these two have
+                     exactly same shape.
+license:             BSD3
+license-file:        LICENSE
+author:              Koji Miyazato
+maintainer:          viercc@gmail.com
+category:            Functors
+build-type:          Simple
+extra-source-files:  README.md, CHANGELOG.md
+cabal-version:       >=1.10
+
+source-repository head
+  type:     git
+  location: https://github.com/viercc/matchable
+  branch:   master
+
+library
+  hs-source-dirs:       src
+  exposed-modules:      Data.Matchable
+  build-depends:        base                 >=4.10      && <5,
+                        containers           >=0.5.10.2  && <0.7,
+                        tagged               >=0.8       && <0.9,
+                        hashable             >=1.0.1.1   && <1.3,
+                        unordered-containers >=0.2.6     && <0.3,
+                        vector               >=0.10.9.0  && <0.13
+  ghc-options:          -Wall
+  default-language:     Haskell2010
+
+test-suite my-functors-doctest
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             doctest.hs
+  build-depends:       base >4 && <5,
+                       containers           >=0.5.10.2  && <0.7,
+                       tagged               >=0.8       && <0.9,
+                       hashable             >=1.0.1.1   && <1.3,
+                       unordered-containers >=0.2.6     && <0.3,
+                       vector               >=0.10.9.0  && <0.13,
+                       Glob, doctest, doctest-discover
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+test-suite examples
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             example.hs
+  build-depends:       base, containers, matchable, hspec
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
diff --git a/src/Data/Matchable.hs b/src/Data/Matchable.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Matchable.hs
@@ -0,0 +1,326 @@
+{-# LANGUAGE EmptyCase        #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE TypeFamilies     #-}
+{-# LANGUAGE TypeOperators    #-}
+{-# LANGUAGE DeriveFunctor    #-}
+module Data.Matchable(
+  -- * Matchable class
+  Matchable(..),
+  zipzipMatch,
+  fmapRecovered,
+  eqDefault,
+  liftEqDefault,
+
+  -- * Define Matchable by Generic
+  Matchable'(), genericZipMatchWith,
+) where
+
+import           Control.Applicative
+
+import           Data.Functor.Classes
+
+import           Data.Maybe (fromMaybe, isJust)
+import           Data.Foldable
+
+import           Data.Functor.Identity
+import           Data.Functor.Compose
+import           Data.Functor.Product
+import           Data.Functor.Sum
+
+import           Data.Tagged
+import           Data.Proxy
+
+import           Data.List.NonEmpty     (NonEmpty)
+
+import           Data.Map.Lazy          (Map)
+import qualified Data.Map.Lazy          as Map
+import           Data.IntMap.Lazy       (IntMap)
+import qualified Data.IntMap.Merge.Lazy as IntMap
+import           Data.Tree              (Tree)
+import           Data.Sequence          (Seq)
+import qualified Data.Sequence          as Seq
+
+import           Data.Vector            (Vector)
+import qualified Data.Vector            as Vector
+
+import           Data.Hashable          (Hashable)
+import           Data.HashMap.Lazy      (HashMap)
+import qualified Data.HashMap.Lazy      as HashMap
+
+import           GHC.Generics
+
+-- | Containers that allows exact structural matching of two containers.
+class (Eq1 t, Functor t) => Matchable t where
+  {- |
+  Decides if two structures match exactly. If they match, return zipped version of them.
+
+  > zipMatch ta tb = Just tab
+
+  holds if and only if both of
+
+  > ta = fmap fst tab
+  > tb = fmap snd tab
+
+  holds. Otherwise, @zipMatch ta tb = Nothing@.
+
+  For example, the type signature of @zipMatch@ on the list Functor @[]@ reads as follows:
+
+  > zipMatch :: [a] -> [b] -> Maybe [(a,b)]
+
+  @zipMatch as bs@ returns @Just (zip as bs)@ if the lengths of two given lists are
+  same, and returns @Nothing@ otherwise.
+
+  ==== Example
+  >>> zipMatch [1, 2, 3] ['a', 'b', 'c']
+  Just [(1,'a'),(2,'b'),(3,'c')]
+  >>> zipMatch [1, 2, 3] ['a', 'b']
+  Nothing
+  -}
+  zipMatch :: t a -> t b -> Maybe (t (a,b))
+  zipMatch = zipMatchWith (curry Just)
+
+  {- |
+  Match two structures. If they match, zip them with given function
+  @(a -> b -> Maybe c)@. Passed function can make whole match fail
+  by returning @Nothing@.
+
+  A definition of 'zipMatchWith' must satisfy:
+
+      * If there is a pair @(tab, tc)@ such that fulfills all following three conditions,
+        then @zipMatchWith f ta tb = Just tc@.
+
+            1. @ta = fmap fst tab@
+            2. @tb = fmap snd tab@
+            3. @fmap (uncurry f) tab = fmap Just tc@
+
+      * If there are no such pair, @zipMatchWith f ta tb = Nothing@.
+
+  If @t@ is also 'Traversable', the last condition can be dropped and
+  the equation can be stated without using @tc@.
+
+  > zipMatchWith f ta tb = traverse (uncurry f) tab
+  
+  @zipMatch@ can be defined in terms of @zipMatchWith@.
+  And if @t@ is also @Traversable@, @zipMatchWith@ can be defined in terms of @zipMatch@.
+  When you implement both of them by hand, keep their relation in the way
+  the default implementation is.
+
+  > zipMatch             = zipMatchWith (curry pure)
+  > zipMatchWith f ta tb = zipMatch ta tb >>= traverse (uncurry f)
+
+  -}
+  zipMatchWith :: (a -> b -> Maybe c) -> t a -> t b -> Maybe (t c)
+
+  {-# MINIMAL zipMatchWith #-}
+
+-- | > zipzipMatch = zipMatchWith zipMatch
+zipzipMatch
+  :: (Matchable t, Matchable u)
+  => t (u a)
+  -> t (u b)
+  -> Maybe (t (u (a, b)))
+zipzipMatch = zipMatchWith zipMatch
+
+-- | @Matchable t@ implies @Functor t@.
+--   It is not recommended to implement @fmap@ through this function,
+--   so it is named @fmapRecovered@ but not @fmapDefault@.
+fmapRecovered :: (Matchable t) => (a -> b) -> t a -> t b
+fmapRecovered f ta =
+  fromMaybe (error "Law-violating Matchable instance") $
+    zipMatchWith (\a _ -> Just (f a)) ta ta
+
+-- | @Matchable t@ implies @Eq a => Eq (t a)@.
+eqDefault :: (Matchable t, Eq a) => t a -> t a -> Bool
+eqDefault = liftEqDefault (==)
+
+-- | @Matchable t@ implies @Eq1 t@.
+liftEqDefault :: (Matchable t) => (a -> b -> Bool) -> t a -> t b -> Bool
+liftEqDefault eq tx ty =
+  let u x y = if x `eq` y then Just () else Nothing
+  in isJust $ zipMatchWith u tx ty
+
+-----------------------------------------------
+
+instance Matchable Identity where
+  zipMatchWith = genericZipMatchWith
+
+instance (Eq k) => Matchable (Const k) where
+  zipMatchWith = genericZipMatchWith
+
+instance (Matchable f, Matchable g) => Matchable (Product f g) where
+  zipMatchWith = genericZipMatchWith
+
+instance (Matchable f, Matchable g) => Matchable (Sum f g) where
+  zipMatchWith = genericZipMatchWith
+
+instance (Matchable f, Matchable g) => Matchable (Compose f g) where
+  zipMatchWith = genericZipMatchWith
+
+instance Matchable Proxy where
+  zipMatchWith _ _ _ = Just Proxy
+
+instance Matchable (Tagged t) where
+  zipMatchWith = genericZipMatchWith
+
+instance Matchable Maybe where
+  zipMatchWith = genericZipMatchWith
+
+instance Matchable [] where
+  zipMatchWith = genericZipMatchWith
+
+instance Matchable NonEmpty where
+  zipMatchWith = genericZipMatchWith
+
+instance (Eq e) => Matchable ((,) e) where
+  zipMatchWith = genericZipMatchWith
+
+instance (Eq e) => Matchable (Either e) where
+  zipMatchWith = genericZipMatchWith
+
+instance (Eq k) => Matchable (Map k) where
+  zipMatchWith u ma mb =
+    Map.fromAscList <$> zipMatchWith (zipMatchWith u) (Map.toAscList ma) (Map.toAscList mb)
+
+instance Matchable IntMap where
+  zipMatchWith u =
+    IntMap.mergeA (IntMap.traverseMissing (\_ _ -> Nothing))
+                  (IntMap.traverseMissing (\_ _ -> Nothing))
+                  (IntMap.zipWithAMatched (const u))
+
+instance Matchable Tree where
+  zipMatchWith = genericZipMatchWith
+
+instance Matchable Seq where
+  zipMatch as bs
+    | Seq.length as == Seq.length bs = Just (Seq.zip as bs)
+    | otherwise                      = Nothing
+  zipMatchWith u as bs
+    | Seq.length as == Seq.length bs = unsafeFillIn u as (Data.Foldable.toList bs)
+    | otherwise                      = Nothing
+
+instance Matchable Vector where
+  zipMatch as bs
+    | Vector.length as == Vector.length bs = Just (Vector.zip as bs)
+    | otherwise                            = Nothing
+
+  zipMatchWith u as bs
+    | Vector.length as == Vector.length bs = Vector.zipWithM u as bs
+    | otherwise                            = Nothing
+
+instance (Eq k, Hashable k) => Matchable (HashMap k) where
+  zipMatch as bs
+    | HashMap.size as == HashMap.size bs =
+        HashMap.traverseWithKey (\k a -> (,) a <$> HashMap.lookup k bs) as
+    | otherwise = Nothing
+  zipMatchWith u as bs
+    | HashMap.size as == HashMap.size bs =
+        HashMap.traverseWithKey (\k a -> u a =<< HashMap.lookup k bs) as
+    | otherwise = Nothing
+
+-- * Generic definition
+
+{-|
+
+An instance of Matchable can be implemened through GHC Generics.
+You only need to do two things: Make your type @Functor@ and @Generic1@.
+
+==== Example
+>>> :set -XDeriveFunctor
+>>> :set -XDeriveGeneric
+>>> :{
+  data MyTree label a = Leaf a | Node label [MyTree label a]
+    deriving (Show, Read, Eq, Ord, Functor, Generic1)
+:}
+
+Then you can use @genericZipMatchWith@ to implement @zipMatchWith@ method.
+You also need @Eq1@ instance, but 'liftEqDefault' is provided.
+
+>>> :{
+  instance (Eq label) => Matchable (MyTree label) where
+    zipMatchWith = genericZipMatchWith
+  instance (Eq label) => Eq1 (MyTree label) where
+    liftEq = liftEqDefault
+  :}
+
+>>> zipMatch (Node "foo" [Leaf 1, Leaf 2]) (Node "foo" [Leaf 'a', Leaf 'b'])
+Just (Node "foo" [Leaf (1,'a'),Leaf (2,'b')])
+>>> zipMatch (Node "foo" [Leaf 1, Leaf 2]) (Node "bar" [Leaf 'a', Leaf 'b'])
+Nothing
+>>> zipMatch (Node "foo" [Leaf 1]) (Node "foo" [])
+Nothing
+
+-}
+class Matchable' t where
+  zipMatchWith' :: (a -> b -> Maybe c) -> t a -> t b -> Maybe (t c)
+
+-- | zipMatchWith via Generics.
+genericZipMatchWith
+  :: (Generic1 t, Matchable' (Rep1 t))
+  => (a -> b -> Maybe c)
+  -> t a
+  -> t b
+  -> Maybe (t c)
+genericZipMatchWith u ta tb = to1 <$> zipMatchWith' u (from1 ta) (from1 tb)
+{-# INLINABLE genericZipMatchWith #-}
+
+instance Matchable' V1 where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' _ a _ = case a of { }
+
+instance Matchable' U1 where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' _ _ _ = pure U1
+
+instance Matchable' Par1 where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (Par1 a) (Par1 b) = Par1 <$> u a b
+
+instance Matchable f => Matchable' (Rec1 f) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (Rec1 fa) (Rec1 fb) = Rec1 <$> zipMatchWith u fa fb
+
+instance (Eq c) => Matchable' (K1 i c) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' _ (K1 ca) (K1 cb)
+    = if ca == cb then pure (K1 ca) else empty
+
+instance Matchable' f => Matchable' (M1 i c f) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (M1 fa) (M1 fb) = M1 <$> zipMatchWith' u fa fb
+
+instance (Matchable' f, Matchable' g) => Matchable' (f :+: g) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (L1 fa) (L1 fb) = L1 <$> zipMatchWith' u fa fb
+  zipMatchWith' u (R1 ga) (R1 gb) = R1 <$> zipMatchWith' u ga gb
+  zipMatchWith' _ _       _       = empty
+
+instance (Matchable' f, Matchable' g) => Matchable' (f :*: g) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (fa :*: ga) (fb :*: gb) =
+    liftA2 (:*:) (zipMatchWith' u fa fb) (zipMatchWith' u ga gb)
+
+instance (Matchable f, Matchable' g) => Matchable' (f :.: g) where
+  {-# INLINABLE zipMatchWith' #-}
+  zipMatchWith' u (Comp1 fga) (Comp1 fgb) =
+    Comp1 <$> zipMatchWith (zipMatchWith' u) fga fgb
+
+-- Utility functions
+
+unsafeFillIn :: (Traversable f) => (a -> b -> Maybe c) -> f a -> [b] -> Maybe (f c)
+unsafeFillIn u as bs = fst <$> runFillIn (traverse (useOne u) as) bs
+
+-- Just a @StateT [b] Maybe@ but avoids to depend on transformers
+newtype FillIn b a = FillIn { runFillIn :: [b] -> Maybe (a, [b]) }
+  deriving (Functor)
+
+instance Applicative (FillIn b) where
+  pure a = FillIn $ \bs -> Just (a, bs)
+  FillIn fx <*> FillIn fy = FillIn $ \bs ->
+    fx bs >>= \(x, bs') ->
+    fy bs' >>= \(y, bs'') -> Just (x y, bs'')
+
+useOne :: (a -> b -> Maybe c) -> a -> FillIn b c
+useOne u a = FillIn $ \bs -> case bs of
+  [] -> Nothing
+  (b:bs') -> u a b >>= \c -> Just (c, bs')
+
diff --git a/test/doctest.hs b/test/doctest.hs
new file mode 100644
--- /dev/null
+++ b/test/doctest.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import           System.FilePath.Glob (glob)
+import           Test.DocTest         (doctest)
+
+main :: IO ()
+main = do
+  glob "src/**/*.hs" >>= doctest
diff --git a/test/example.hs b/test/example.hs
new file mode 100644
--- /dev/null
+++ b/test/example.hs
@@ -0,0 +1,118 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveGeneric #-}
+module Main(main) where
+
+import           Data.Matchable
+import qualified Data.Map       as Map
+
+import GHC.Generics(Generic1)
+import Data.Functor.Classes
+
+import Test.Hspec
+
+main :: IO ()
+main = hspec $
+  do context "Matchable []" $ do
+       runIO $ do
+         p $ "list1 = " ++ show list1
+         p $ "list2 = " ++ show list2
+         p $ "list3 = " ++ show list3
+         p   "list4 = repeat 0"
+       specify "zipMatch list1 list2 = Just [(0,3), (1,4), (2,5)]" $
+         zipMatch list1 list2 `shouldBe` Just [(0,3), (1,4), (2,5)]
+       specify "zipMatch list1 list3 = Nothing" $
+         zipMatch list1 list3 `shouldBe` Nothing
+       specify "zipMatch list1 list4 = Nothing" $
+         zipMatch list1 list4 `shouldBe` Nothing
+     context "Matchable (Either String)" $ do
+       runIO $ do
+         p $ "either1 = " ++ show either1
+         p $ "either2 = " ++ show either2
+         p $ "either3 = " ++ show either3
+         p $ "either4 = " ++ show either4
+       specify "zipMatch either1 either2 = Just (Right (1,2))" $
+         zipMatch either1 either2 `shouldBe` Just (Right (1,2))
+       specify "zipMatch either1 either3 = Nothing" $
+         zipMatch either1 either3 `shouldBe` Nothing
+       specify "zipMatch either3 either3 = Just (Left \"foo\")" $
+         zipMatch either3 either3 `shouldBe` Just (Left "foo")
+       specify "zipMatch either3 either4 = Nothing" $
+         zipMatch either3 either4 `shouldBe` Nothing
+     context "Matchable ((,) Char)" $ do
+       runIO $ do
+         p $ "pair1 = " ++ show pair1
+         p $ "pair2 = " ++ show pair2
+         p $ "pair3 = " ++ show pair3
+         p $ "pair4 = " ++ show pair4
+       specify "zipMatch pair1 pair2 = Just ('a', (1,2))" $
+         zipMatch pair1 pair2 `shouldBe` Just ('a', (1,2))
+       specify "zipMatch pair3 pair4 = Just ('b', (3,4))" $
+         zipMatch pair3 pair4 `shouldBe` Just ('b', (3,4))
+       specify "zipMatch pair1 pair3 = Nothing" $
+         zipMatch pair1 pair3 `shouldBe` Nothing
+     context "Matchable (Map Char)" $ do
+       runIO $ do
+         p $ "map1 = " ++ show map1
+         p $ "map2 = " ++ show map2
+         p $ "map3 = " ++ show map3
+       specify "zipMatch map1 map2 = Just (Map.fromList [('a', (1,2)), ('b', (3,4))])" $
+         zipMatch map1 map2 `shouldBe` Just (Map.fromList [('a', (1,2)), ('b', (3,4))])
+       specify "zipMatch map1 map3 = Nothing" $
+         zipMatch map1 map3 `shouldBe` Nothing
+     context "Matchable (MyTree Int)" $ do
+       runIO $ do
+         p $ "myTree1 = " ++ show myTree1
+         p $ "myTree2 = " ++ show myTree2
+         p $ "myTree3 = " ++ show myTree3
+         p $ "myTree4 = " ++ show myTree4
+       specify "zipMatch myTree1 myTree2 = Just _" $
+         zipMatch myTree1 myTree2 `shouldBe` Just (Node 0 ("foo",200) Empty (Node 1 ("bar", 300) Empty Empty))
+       specify "zipMatch myTree1 myTree3 = Nothing" $
+         zipMatch myTree1 myTree3 `shouldBe` Nothing
+       specify "zipMatch myTree1 myTree4 = Nothing" $
+         zipMatch myTree1 myTree4 `shouldBe` Nothing
+  where p = putStrLn
+
+list1, list2, list3, list4 :: [Int]
+list1 = [0,1,2]
+list2 = [3,4,5]
+list3 = [0,1]
+list4 = repeat 0
+
+either1, either2, either3, either4 :: Either String Int
+either1 = Right 1
+either2 = Right 2
+either3 = Left "foo"
+either4 = Left "bar"
+
+pair1, pair2, pair3, pair4 :: (Char, Int)
+pair1 = ('a', 1)
+pair2 = ('a', 2)
+pair3 = ('b', 3)
+pair4 = ('b', 4)
+
+map1, map2, map3 :: Map.Map Char Int
+map1 = Map.fromList [pair1, pair3]
+map2 = Map.fromList [pair2, pair4]
+map3 = Map.fromList [pair1]
+
+data MyTree k a = Empty | Node k a (MyTree k a) (MyTree k a)
+  deriving (Show, Eq, Functor, Generic1)
+
+instance Eq k => Eq1 (MyTree k) where
+  liftEq = liftEqDefault
+
+instance Eq k => Matchable (MyTree k) where
+  zipMatchWith = genericZipMatchWith
+
+myTree1 :: MyTree Int String
+myTree1 = Node 0 "foo" Empty (Node 1 "bar" Empty Empty)
+
+myTree2 :: MyTree Int Int
+myTree2 = Node 0 200 Empty (Node 1 300 Empty Empty)
+
+myTree3 :: MyTree Int Bool
+myTree3 = Node 3 True Empty (Node 4 False Empty Empty)
+
+myTree4 :: MyTree Int Char
+myTree4 = Node 0 'a' Empty Empty
