diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,4 @@
+# 0.4.1.1
+
+- Initial release.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2018, Koji Miyazato
+
+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,34 @@
+# trie-simple
+
+Trie data structure `TMap` to hold mapping from list of characters to
+something, i.e. isomorphic to `Map [c] v`.
+This package also contains `TSet`, which is isomorphic to `Set` of lists of
+characters.
+
+This package implements these structures using `Map` from containers
+package, and require the character type to be only `Ord`.
+
+Advantages of using this package over `Map` or `Set` are:
+
+  * 2x Faster `lookup` (`member`) operation
+  * Retrieving subset of map or set with given prefix
+  * `append`, `prefixes`, and `suffixes` support
+  * Can be more memory-efficient (but not always; needs
+    benchmark anyway).
+
+## Benchmarks
+
+Benchmarks compared against plain `Map` and `Set`.
+
+![benchmark chart for TMap](https://raw.githubusercontent.com/viercc/trie-simple/master/doc/benchTMap.png)
+
+![benchmark chart for TSet](https://raw.githubusercontent.com/viercc/trie-simple/master/doc/benchTSet.png)
+
+## About License
+
+[LICENSE](LICENSE) tells the licence of this project, EXCEPT
+one file for benchmark input data. See [ABOUT](ABOUT) for that
+file.
+
+If you install `trie-simple` from Hackage, that input data is not
+included in the distributed files.
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/bench/Common.hs b/bench/Common.hs
new file mode 100644
--- /dev/null
+++ b/bench/Common.hs
@@ -0,0 +1,43 @@
+module Common(
+  dictAmEn, dictBrEn,
+  dictAmEnShuffled, randomStrs,
+  dictURI1, dictURI2
+) where
+
+import qualified Data.Vector as V
+import Data.Word
+
+import qualified System.Random.MWC                as R
+import qualified System.Random.MWC.CondensedTable as R
+import qualified System.Random.MWC.Distributions  as R
+
+numRandomStr :: Int
+numRandomStr = 1000
+
+seed :: Word32 -> V.Vector Word32
+seed w = V.fromList [1573289798, 32614861, w]
+
+dictAmEn, dictBrEn, dictAmEnShuffled, randomStrs :: IO [String]
+dictAmEn = lines <$> readFile "/usr/share/dict/american-english"
+dictBrEn = lines <$> readFile "/usr/share/dict/british-english"
+dictAmEnShuffled = lines <$> readFile "benchdata/american-english-shuf"
+randomStrs =
+  do g <- R.initialize (seed 3)
+     revReplicateM numRandomStr $ do
+       n <- R.genFromTable distN g
+       revReplicateM (n+1) (uniformAlphabet g)
+  where
+    distN = R.tableBinomial 12 0.33
+    alphabet = V.fromList ['a' .. 'z']
+    numAlphabet = V.length alphabet
+    uniformAlphabet g = (alphabet V.!) <$> R.uniformR (0, numAlphabet-1) g
+
+dictURI1, dictURI2 :: IO [String]
+dictURI1 = lines <$> readFile "benchdata/externallinks.txt.1"
+dictURI2 = lines <$> readFile "benchdata/externallinks.txt.2"
+
+revReplicateM :: (Monad m) => Int -> m a -> m [a]
+revReplicateM n ma = loop n []
+  where
+    loop 0 acc = return acc
+    loop i acc = ma >>= \a -> loop (i-1) (a:acc)
diff --git a/bench/trie-benchmark.hs b/bench/trie-benchmark.hs
new file mode 100644
--- /dev/null
+++ b/bench/trie-benchmark.hs
@@ -0,0 +1,280 @@
+module Main(main) where
+
+import Criterion.Main
+
+import qualified Data.Trie.Set as TSet
+import Data.Trie.Map (TMap)
+import qualified Data.Trie.Map as TMap
+
+import Data.Monoid
+import Data.List (sort, inits, tails, foldl')
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Map.Lazy (Map)
+import qualified Data.Map.Lazy as Map
+
+import Common
+
+main :: IO ()
+main = defaultMain
+  [ benchTSet
+  , benchTSet_URI
+  , benchSet
+  , benchSet_URI
+  , benchTMap
+  , benchMap
+  ]
+
+benchTSet :: Benchmark
+benchTSet = bgroup "TSet" 
+  [ bgroup "construction"
+      [ env dictAmEnShuffled $ \dict ->
+          bench "fromList" $ whnf TSet.fromList dict
+      , env dictAmEn $ \sortedDict ->
+          bench "fromAscList" $ whnf TSet.fromAscList sortedDict ]
+  , env (TSet.fromList <$> dictAmEn) $ \dict ->
+      bgroup "query"
+        [ bench "isEmpty" (nf TSet.null dict)
+        , bench "stringCount" (nf TSet.count dict)
+        , bench "enumerate10" (nf (take 10 . TSet.enumerate) dict)
+        , bench "enumerateAll" (nf TSet.enumerate dict)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dict' -> map (`TSet.member` dict') qs) dict) ]
+  , env (TSet.fromList <$> dictAmEn) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (TSet.insert "wwwwwwwwwwwwwwww") dict)
+        , bench "insert2" (whnf (TSet.insert "cheese") dict)
+        , bench "delete1" (whnf (TSet.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (TSet.delete "cheese") dict)
+        ]
+  , env (TSet.fromList <$> dictAmEn) $ \dictA ->
+    env (TSet.fromList <$> dictBrEn) $ \dictB ->
+    env (TSet.fromList <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry TSet.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry TSet.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry TSet.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry TSet.append) (dictSmall, dictSmall))
+        , bench "prefixes" (whnf TSet.prefixes dictA)
+        , bench "suffixes" (whnf TSet.suffixes dictB) ]
+  ]
+
+benchTSet_URI :: Benchmark
+benchTSet_URI = bgroup "TSet_URI" 
+  [ bgroup "construction"
+      [ env dictURI1 $ \dict ->
+          bench "fromList" $ whnf TSet.fromList dict
+      , env (sort <$> dictURI1) $ \sortedDict ->
+          bench "fromAscList" $ whnf TSet.fromAscList sortedDict ]
+  , env (TSet.fromList <$> dictURI1) $ \dict ->
+      bgroup "query"
+        [ bench "isEmpty" (nf TSet.null dict)
+        , bench "stringCount" (nf TSet.count dict)
+        , bench "enumerate10" (nf (take 10 . TSet.enumerate) dict)
+        , bench "enumerateAll" (nf TSet.enumerate dict)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dict' -> map (`TSet.member` dict') qs) dict) ]
+  , env (TSet.fromList <$> dictURI1) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (TSet.insert "wwwwwwwwwwwwwwww") dict)
+        , bench "insert2" (whnf (TSet.insert "cheese") dict)
+        , bench "delete1" (whnf (TSet.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (TSet.delete "cheese") dict)
+        ]
+  , env (TSet.fromList <$> dictURI1) $ \dictA ->
+    env (TSet.fromList <$> dictURI2) $ \dictB ->
+    env (TSet.fromList <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry TSet.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry TSet.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry TSet.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry TSet.append) (dictSmall, dictSmall))
+        , bench "prefixes" (whnf TSet.prefixes dictA)
+        , bench "suffixes" (whnf TSet.suffixes dictB) ]
+  ]
+  
+benchSet :: Benchmark
+benchSet = bgroup "Set" 
+  [ bgroup "construction"
+      -- Set.fromList detects whether the input list is sorted
+      -- and switch the algorithm based on it.
+      -- Using shuffled dictionary avoids this optimization fires
+      -- in this benchmark.
+      [ env dictAmEnShuffled $ \dict ->
+          bench "fromList" $ whnf Set.fromList dict
+      , env dictAmEn $ \sortedDict ->
+          bench "fromAscList" $ whnf Set.fromAscList sortedDict ]
+  , env (Set.fromList <$> dictAmEn) $ \dictSet ->
+      bgroup "query"
+        [ bench "isEmpty" (nf Set.null dictSet)
+        , bench "stringCount" (nf Set.size dictSet)
+        , bench "enumerate10" (nf (take 10 . Set.toList) dictSet)
+        , bench "enumerateAll" (nf Set.toList dictSet)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dictSet' -> map (`Set.member` dictSet') qs) dictSet) ]
+  , env (Set.fromList <$> dictAmEn) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (Set.insert "wwwwwwwwwwwwwwww") dict)
+        , bench "insert2" (whnf (Set.insert "cheese") dict)
+        , bench "delete1" (whnf (Set.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (Set.delete "cheese") dict)
+        ]
+  , env (Set.fromList <$> dictAmEn) $ \dictA ->
+    env (Set.fromList <$> dictBrEn) $ \dictB ->
+    env (Set.fromList <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry Set.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry Set.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry Set.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry setAppend) (dictSmall, dictSmall))
+        , bench "prefixes" (whnf setPrefixes dictA)
+        , bench "suffixes" (whnf setSuffixes dictB) ]
+  ]
+
+benchSet_URI :: Benchmark
+benchSet_URI = bgroup "Set_URI" 
+  [ bgroup "construction"
+      -- Set.fromList detects whether the input list is sorted
+      -- and switch the algorithm based on it.
+      -- Using shuffled dictionary avoids this optimization fires
+      -- in this benchmark.
+      [ env dictURI1 $ \dict ->
+          bench "fromList" $ whnf Set.fromList dict
+      , env (sort <$> dictURI1) $ \sortedDict ->
+          bench "fromAscList" $ whnf Set.fromAscList sortedDict ]
+  , env (Set.fromList <$> dictURI1) $ \dictSet ->
+      bgroup "query"
+        [ bench "isEmpty" (nf Set.null dictSet)
+        , bench "stringCount" (nf Set.size dictSet)
+        , bench "enumerate10" (nf (take 10 . Set.toList) dictSet)
+        , bench "enumerateAll" (nf Set.toList dictSet)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dictSet' -> map (`Set.member` dictSet') qs) dictSet) ]
+  , env (Set.fromList <$> dictURI1) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (Set.insert "wwwwwwwwwwwwwwww") dict)
+        , bench "insert2" (whnf (Set.insert "cheese") dict)
+        , bench "delete1" (whnf (Set.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (Set.delete "cheese") dict)
+        ]
+  , env (Set.fromList <$> dictURI1) $ \dictA ->
+    env (Set.fromList <$> dictURI2) $ \dictB ->
+    env (Set.fromList <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry Set.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry Set.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry Set.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry setAppend) (dictSmall, dictSmall))
+        , bench "prefixes" (whnf setPrefixes dictA)
+        , bench "suffixes" (whnf setSuffixes dictB) ]
+  ]
+
+setAppend :: (Ord c) => Set [c] -> Set [c] -> Set [c]
+setAppend ass bss = Set.unions
+  [ Set.mapMonotonic (as ++) bss
+      | as <- Set.toAscList ass ]
+
+setPrefixes :: (Ord c) => Set [c] -> Set [c]
+setPrefixes ass = Set.unions
+  [ Set.fromDistinctAscList (inits as) | as <- Set.toAscList ass ]
+
+setSuffixes :: (Ord c) => Set [c] -> Set [c]
+setSuffixes ass = Set.fromList
+  [ bs | as <- Set.toAscList ass, bs <- tails as ]
+
+
+benchTMap :: Benchmark
+benchTMap = bgroup "TMap" 
+  [ bgroup "construction"
+      [ env dictAmEnShuffled $ \dict ->
+          bench "fromList" $ whnf TMap.fromList [(w, length w) | w <- dict]
+      , env (sort <$> dictAmEn) $ \sortedDict ->
+          bench "fromAscList" $ whnf TMap.fromAscList [(w, length w) | w <- sortedDict]
+      ]
+  , env (lenTMap <$> dictAmEn) $ \dict ->
+      bgroup "query"
+        [ bench "isEmpty" (nf TMap.null dict)
+        , bench "stringCount" (nf TMap.count dict)
+        , bench "enumerate10" (nf (take 10 . TMap.toList) dict)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dict' -> map (`TMap.member` dict') qs) dict) ]
+  , env (lenTMap <$> dictAmEn) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (TMap.insert "wwwwwwwwwwwwwwww" 1) dict)
+        , bench "insert2" (whnf (TMap.insert "cheese" 1) dict)
+        , bench "delete1" (whnf (TMap.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (TMap.delete "cheese") dict)
+        , bench "alter1" (whnf (TMap.alter alterFn "wwwwwwwwwwwwwwww") dict)
+        , bench "alter2" (whnf (TMap.alter alterFn "cheese") dict)
+        ]
+  , env (lenTMap <$> dictAmEn) $ \dict ->
+      bgroup "traversal"
+        [ bench "fmap" (nf (fmap (+3)) dict)
+        , bench "foldMap" (nf (foldMap Sum) dict) ]
+  , env (lenTMap <$> dictAmEn) $ \dictA ->
+    env (lenTMap <$> dictBrEn) $ \dictB ->
+    env (lenTMap <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry TMap.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry TMap.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry TMap.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry tmapProd) (dictSmall, dictSmall)) ]
+  ]
+
+alterFn :: Maybe Int -> Maybe Int
+alterFn Nothing = Just 1000
+alterFn (Just a) = if even a then Just a else Nothing
+
+lenTMap :: (Ord c) => [[c]] -> TMap c Int
+lenTMap dict = TMap.fromList [(w, length w) | w <- dict]
+
+tmapProd :: (Ord c) => TMap c Int -> TMap c Int -> TMap c (Sum Int)
+tmapProd t1 t2 = TMap.appendWith (*) (Sum <$> t1) (Sum <$> t2)
+
+benchMap :: Benchmark
+benchMap = bgroup "Map" 
+  [ bgroup "construction"
+      [ env dictAmEnShuffled $ \dict ->
+          bench "fromList" $ whnf Map.fromList [(w, length w) | w <- dict]
+      , env (sort <$> dictAmEn) $ \sortedDict ->
+          bench "fromAscList" $ whnf Map.fromAscList [(w, length w) | w <- sortedDict]
+      ]
+  , env (lenMap <$> dictAmEn) $ \dict ->
+      bgroup "query"
+        [ bench "isEmpty" (nf Map.null dict)
+        , bench "stringCount" (nf Map.size dict)
+        , bench "enumerate10" (nf (take 10 . Map.toList) dict)
+        , env randomStrs $ \qs ->
+            bench "match" (nf (\dict' -> map (`Map.member` dict') qs) dict) ]
+  , env (lenMap <$> dictAmEn) $ \dict ->
+      bgroup "single-item"
+        [ bench "insert1" (whnf (Map.insert "wwwwwwwwwwwwwwww" 1) dict)
+        , bench "insert2" (whnf (Map.insert "cheese" 1) dict)
+        , bench "delete1" (whnf (Map.delete "wwwwwwwwwwwwwwww") dict)
+        , bench "delete2" (whnf (Map.delete "cheese") dict)
+        , bench "alter1" (whnf (Map.alter alterFn "wwwwwwwwwwwwwwww") dict)
+        , bench "alter2" (whnf (Map.alter alterFn "cheese") dict)
+        ]
+  , env (lenMap <$> dictAmEn) $ \dict ->
+      bgroup "traversal"
+        [ bench "fmap" (nf (fmap (+3)) dict)
+        , bench "foldMap" (nf (foldMap Sum) dict) ]
+  , env (lenMap <$> dictAmEn) $ \dictA ->
+    env (lenMap <$> dictBrEn) $ \dictB ->
+    env (lenMap <$> randomStrs) $ \dictSmall ->
+      bgroup "combine"
+        [ bench "union" (whnf (uncurry Map.union) (dictA, dictB))
+        , bench "intersection" (whnf (uncurry Map.intersection) (dictA, dictB))
+        , bench "difference" (whnf (uncurry Map.difference) (dictA, dictB))
+        , bench "append" (whnf (uncurry mapProd) (dictSmall, dictSmall)) ]
+  ]
+
+lenMap :: (Ord c) => [[c]] -> Map [c] Int
+lenMap dict = Map.fromList [(w, length w) | w <- dict]
+
+mapProd :: (Ord c) => Map [c] Int -> Map [c] Int -> Map [c] Int
+mapProd m1 m2 =
+  foldl' (Map.unionWith (+)) Map.empty
+    [ prod1 s x m2 | (s,x) <- Map.toList m1 ]
+  where
+    prod1 s x m = Map.mapKeysMonotonic (s++) $ Map.map (x*) m
diff --git a/src/Data/Trie/Map.hs b/src/Data/Trie/Map.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Map.hs
@@ -0,0 +1,45 @@
+module Data.Trie.Map(
+  -- * Type
+  TMap(),
+  -- * Queries
+  match,
+  lookup,
+  member, notMember,
+  null, count,
+  keys, elems,
+  -- * Construction
+  empty, just,
+  singleton,
+
+  -- * Single item modification
+  insertWith, insert,
+  deleteWith, delete,
+
+  adjust, revise, update, alter,
+
+  -- * Combine
+  --
+  -- These functions behave in the same way as corresponding
+  -- functions from "Data.Map".
+  union, unionWith,
+  intersection, intersectionWith,
+  difference, differenceWith,
+  appendWith,
+
+  -- * Conversion
+  toList, fromList,
+  toAscList, fromAscList,
+  toMap, fromMap,
+  keysTSet, fromTSet,
+
+  -- * Parsing
+  toParser, toParser_, toParser__,
+
+  -- * Traversing with keys
+  traverseWithKey, mapWithKey, foldMapWithKey, foldrWithKey,
+)
+where
+
+import           Prelude              hiding (lookup, null)
+
+import           Data.Trie.Map.Hidden
diff --git a/src/Data/Trie/Map/Hidden.hs b/src/Data/Trie/Map/Hidden.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Map/Hidden.hs
@@ -0,0 +1,515 @@
+{-# LANGUAGE DeriveTraversable #-}
+module Data.Trie.Map.Hidden(
+  -- * Types
+  TMap(..),
+  -- * Queries
+  match,
+  lookup,
+  member, notMember,
+  null, count,
+  keys, elems,
+  -- * Construction
+  empty, just,
+  singleton,
+
+  -- * Single item modification
+  insertWith, insert,
+  deleteWith, delete,
+
+  adjust, revise, update, alter,
+
+  -- * Combine
+  union, unionWith,
+  intersection, intersectionWith,
+  difference, differenceWith,
+  appendWith,
+
+  -- * Conversion
+  toList, fromList,
+  toAscList, fromAscList,
+  toMap, fromMap,
+  keysTSet, fromTSet,
+
+  -- * Parsing
+  toParser, toParser_, toParser__,
+
+  -- * Traversing with keys
+  traverseWithKey, mapWithKey, foldMapWithKey, foldrWithKey,
+
+  -- * Internals
+  Node(..),
+  foldTMap,
+)
+where
+
+import           Prelude                hiding (lookup, null)
+
+import           Data.Functor.Const
+import           Data.Functor.Identity
+
+import           Data.Semigroup
+
+import           Control.Applicative    hiding (empty)
+import qualified Control.Applicative    as Ap (empty)
+
+import           Control.Monad
+
+import qualified Data.Foldable          as F
+import qualified Data.List              as List (foldl')
+import           Data.Map.Strict        (Map)
+import qualified Data.Map.Strict        as Map
+import           Data.Maybe             (fromMaybe, isJust, isNothing)
+
+import           Data.Trie.Set.Internal (TSet (..))
+import qualified Data.Trie.Set.Internal as TSet
+
+import           Control.DeepSeq
+
+data Node c a r = Node !(Maybe a) !(Map c r)
+  deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
+
+instance (NFData c, NFData a, NFData r) => NFData (Node c a r) where
+  rnf (Node a e) = rnf a `seq` rnf e
+
+-- | Mapping from @[c]@ to @a@ implemented as a trie.
+--   This type serves almost same purpose with @Map [c] a@,
+--   but can be looked up more efficiently.
+newtype TMap c a = TMap { getNode :: Node c a (TMap c a) }
+  deriving (Eq, Ord)
+
+instance (Show c, Show a) => Show (TMap c a) where
+  showsPrec p t = showParen (p > 10) $ showString "fromList " . showsPrec 11 (toList t)
+
+instance (NFData c, NFData a) => NFData (TMap c a) where
+  rnf (TMap node) = rnf node
+
+-- * Queries
+
+-- | Perform matching against a @TMap@.
+--
+--   @match xs tmap@ returns two values. First value is the result of
+--   'lookup'. Second value is another @TMap@, which holds mapping between
+--   all pair of @ys@ and @b@, such that @tmap@ maps @(xs ++ ys)@ to @b@.
+match :: (Ord c) => [c] -> TMap c a -> (Maybe a, TMap c a)
+match []     t@(TMap (Node ma _)) = (ma, t)
+match (c:cs)   (TMap (Node _  e)) =
+  case Map.lookup c e of
+    Nothing -> (Nothing, empty)
+    Just t' -> match cs t'
+
+-- | @lookup xs tmap@ returns @Just a@ if @tmap@ contains mapping
+--   from @xs@ to @a@, and returns @Nothing@ if not.
+lookup :: (Ord c) => [c] -> TMap c a -> Maybe a
+lookup cs = fst . match cs
+
+member, notMember :: (Ord c) => [c] -> TMap c a -> Bool
+member cs = isJust . lookup cs
+notMember cs = isNothing . lookup cs
+
+-- | Tests if given map is empty.
+null :: TMap c a -> Bool
+null (TMap (Node ma e)) = isNothing ma && Map.null e
+{- Ensure all @TMap@ values exposed to users have no
+   redundant node. -}
+
+-- | Returns number of entries.
+--
+--   Note that this operation takes O(number of nodes),
+--   unlike O(1) of 'Map.size'.
+count :: TMap c a -> Int
+count = F.length
+
+-- | Returns list of key strings, in ascending order.
+keys :: TMap c a -> [[c]]
+keys = foldTMap keys'
+  where
+    keys' (Node ma e) =
+      [ [] | isJust ma ] ++
+      [ c:cs' | (c,css') <- Map.toList e, cs' <- css' ]
+
+-- | Returns list of values, in ascending order by its key.
+elems :: TMap c a -> [a]
+elems = F.toList
+
+-- * Construction
+
+-- | Empty @TMap@.
+empty :: TMap c a
+empty = TMap (Node Nothing Map.empty)
+
+-- | @TMap@ which contains only one entry from the empty string to @a@.
+just :: a -> TMap c a
+just a = TMap (Node (Just a) Map.empty)
+
+-- | @singleton xs a@ is a @TMap@ which contains only one entry
+--   from @xs@ to @a@.
+singleton :: [c] -> a -> TMap c a
+singleton cs a0 = foldr cons (just a0) cs
+
+cons :: c -> TMap c a -> TMap c a
+cons c t = TMap (Node Nothing (Map.singleton c t))
+
+-- * Single-item modification
+
+-- | Inserts an entry of key and value pair.
+--
+--   Already existing value will be overwritten, i.e.
+--   > insert = insertWith (const a)
+insert :: (Ord c) => [c] -> a -> TMap c a -> TMap c a
+insert cs a = revise (const a) cs
+
+-- | Deletes an entry with given key.
+--
+--   > delete = update (const Nothing)
+delete :: (Ord c) => [c] -> TMap c a -> TMap c a
+delete = update (const Nothing)
+
+-- | @insertWith op xs a tmap@ inserts an key (@xs@) and value (@a@) pair
+--   to the @tmap@. If @tmap@ already has an entry with key equals to
+--   @xs@, its value @b@ is replaced with @op a b@.
+--
+--   > insertWith op cs a = revise (maybe a (op a)) cs
+insertWith :: (Ord c) => (a -> a -> a) -> [c] -> a -> TMap c a -> TMap c a
+insertWith f cs a = revise (maybe a (f a)) cs
+
+-- | Deletes an entry with given key, conditionally.
+--
+--   @deleteWith f xs b@ looks up an entry with key @xs@, and if such entry
+--   is found, evaluate @f b a@ with its value @a@. If it returned @Nothing@,
+--   the entry is deleted. Otherwise, if it returned @Just a'@, the value of
+--   the entry is replaced with @a'@.
+--
+--   > deleteWith f cs b = update (f b) cs
+deleteWith :: (Ord c) => (b -> a -> Maybe a) -> [c] -> b -> TMap c a -> TMap c a
+deleteWith f cs b = update (f b) cs
+
+-- | Apply a function to the entry with given key.
+adjust :: (Ord c) => (a -> a) -> [c] -> TMap c a -> TMap c a
+adjust f = F.foldr step base
+  where
+    base (TMap (Node ma e)) = TMap (Node (f <$> ma) e)
+    step x xs (TMap (Node ma e)) =
+      let e' = Map.adjust xs x e
+      in TMap (Node ma e')
+{-# INLINE adjust #-}
+
+-- | Apply a function @f@ to the entry with given key. If there is no such
+--   entry, insert an entry with value @f Nothing@.
+revise :: (Ord c) => (Maybe a -> a) -> [c] -> TMap c a -> TMap c a
+revise f = fst . F.foldr step (base, just (f Nothing))
+  where
+    base (TMap (Node ma e)) = TMap (Node (Just (f ma)) e)
+    step x (inserter', xs') =
+      let inserter (TMap (Node ma e)) =
+            let e' = Map.insertWith (const inserter') x xs' e
+            in TMap (Node ma e')
+      in (inserter, cons x xs')
+{-# INLINE revise #-}
+
+-- | Apply a function @f@ to the entry with given key. If @f@ returns
+--   @Nothing@, that entry is deleted.
+update :: (Ord c) => (a -> Maybe a) -> [c] -> TMap c a -> TMap c a
+update f cs = fromMaybe empty . update_ f cs
+{-# INLINE update #-}
+
+update_ :: (Ord c) => (a -> Maybe a) -> [c] -> TMap c a -> Maybe (TMap c a)
+update_ f = F.foldr step base
+  where
+    base (TMap (Node ma e)) =
+      let ma' = ma >>= f
+      in if isNothing ma' && Map.null e
+           then Nothing
+           else Just $ TMap (Node ma' e)
+    step x xs (TMap (Node ma e)) =
+      let e' = Map.update xs x e
+      in if isNothing ma && Map.null e'
+           then Nothing
+           else Just $ TMap (Node ma e')
+{-# INLINE update_ #-}
+
+-- | Apply a function @f@ to the entry with given key. This function @alter@
+--   is the most generic version of 'adjust', 'revise', 'update'.
+-- 
+--   * You can insert new entry by returning @Just a@ from @f Nothing@.
+--   * You can delete existing entry by returning @Nothing@ from
+--     @f (Just a)@.
+--
+--   This function always evaluates @f Nothing@ in addition to determine
+--   operation applied to given key.
+--   If you never use `alter` on a missing key, consider using 'update' instead.
+alter :: (Ord c) => (Maybe a -> Maybe a) -> [c] -> TMap c a -> TMap c a
+alter f =
+  case f Nothing of
+    Nothing -> update (f . Just)
+    Just f0 -> \cs -> fromMaybe empty . alter_ f f0 cs
+{-# INLINE alter #-}
+
+alter_ :: (Ord c) => (Maybe a -> Maybe a) -> a -> [c] -> TMap c a -> Maybe (TMap c a)
+alter_ f f0 = fst . F.foldr step (base, just f0)
+  where
+    base (TMap (Node ma e)) =
+      let ma' = f ma
+      in if isNothing ma' && Map.null e
+           then Nothing
+           else Just $ TMap (Node ma' e)
+    step x (alterer', xs') =
+      let alterer (TMap (Node ma e)) =
+            let e' = Map.alter (maybe (Just xs') alterer') x e
+            in if isNothing ma && Map.null e'
+                 then Nothing
+                 else Just $ TMap (Node ma e')
+      in (alterer, cons x xs')
+{-# INLINE alter_ #-}
+
+-- * Combine
+union :: (Ord c) => TMap c a -> TMap c a -> TMap c a
+union = unionWith const
+
+unionWith :: (Ord c) => (a -> a -> a) -> TMap c a -> TMap c a -> TMap c a
+unionWith f = go
+  where
+    go (TMap (Node mat et)) (TMap (Node mau eu)) =
+      let maz = case (mat, mau) of
+            (Nothing, Nothing) -> Nothing
+            (Just at, Nothing) -> Just at
+            (Nothing, Just au) -> Just au
+            (Just at, Just au) -> Just (f at au)
+          ez = Map.unionWith go et eu
+      in TMap (Node maz ez)
+
+intersection :: (Ord c) => TMap c a -> TMap c b -> TMap c a
+intersection = intersectionWith (\a _ -> Just a)
+
+intersectionWith :: (Ord c) =>
+  (a -> b -> Maybe r) -> TMap c a -> TMap c b -> TMap c r
+intersectionWith f x y = fromMaybe empty $ go x y
+  where
+    go (TMap (Node ma ex)) (TMap (Node mb ey)) =
+      if isNothing mr && Map.null ez
+        then Nothing
+        else Just $ TMap (Node mr ez)
+      where
+        mr = do a <- ma
+                b <- mb
+                f a b
+        emz = Map.intersectionWith go ex ey
+        ez = Map.mapMaybe id emz
+
+difference :: (Ord c) => TMap c a -> TMap c b -> TMap c a
+difference = differenceWith (\_ _ -> Nothing)
+
+differenceWith :: (Ord c) =>
+  (a -> b -> Maybe a) -> TMap c a -> TMap c b -> TMap c a
+differenceWith f x y = fromMaybe empty $ go x y
+  where
+    go (TMap (Node ma ex)) (TMap (Node mb ey)) =
+      if isNothing mr && Map.null ez
+        then Nothing
+        else Just $ TMap (Node mr ez)
+      where
+        mr = case (ma, mb) of
+          (Nothing, _)       -> Nothing
+          (Just a,  Nothing) -> Just a
+          (Just a,  Just b)  -> f a b
+        ez = Map.differenceWith go ex ey
+
+{- |
+Make new @TMap@ from two @TMap@s. Constructed @TMap@
+has keys which are concatenation of any combination from
+two input maps.
+
+Corresponding values for these keys are combined with given function
+of type @(x -> y -> z)@. If two different concatenations yield
+a same key, corresponding values for these keys are combined with
+a 'Semigroup' operation @<>@.
+
+There is no guarantees on which order duplicate values are combined with @<>@.
+So it must be commutative semigroup to get a stable result.
+
+===== Example
+
+> let x = fromList [("a", 1), ("aa", 2)]     :: TMap Char (Sum Int)
+>     y = fromList [("aa", 10), ("aaa", 20)] :: TMap Char (Sum Int)
+>
+> appendWith (*) x y =
+>   fromList [ ("aaa", 1 * 10)
+>            , ("aaaa", 1 * 20 + 2 * 10)
+>            , ("aaaaa", 2 * 20) ]
+
+-}
+appendWith :: (Ord c, Semigroup z) => (x -> y -> z) ->
+  TMap c x -> TMap c y -> TMap c z
+appendWith f x y =
+  if null y
+    then empty
+    else go x
+  where
+    go (TMap (Node Nothing e)) =
+      let e' = Map.map go e
+      in TMap (Node Nothing e')
+    go (TMap (Node (Just ax) e)) =
+      let TMap (Node maz e') = fmap (f ax) y
+          e'' = Map.map go e
+          e''' = Map.unionWith (unionWith (<>)) e' e''
+      in TMap (Node maz e''')
+
+-- * Instances
+
+instance Functor (TMap c) where
+  fmap f = go
+    where
+      go (TMap (Node ma e)) = TMap (Node (fmap f ma) (fmap go e))
+
+instance Foldable (TMap c) where
+  foldMap f = go
+    where
+      go (TMap (Node ma e)) = case ma of
+        Nothing -> foldMap go e
+        Just a  -> f a `mappend` foldMap go e
+
+instance Traversable (TMap c) where
+  traverse f = go
+    where
+      go (TMap (Node a e)) = TMap <$> (Node <$> traverse f a <*> traverse go e)
+
+-- | 'unionWith'-based
+instance (Ord c, Semigroup a) => Semigroup (TMap c a) where
+  (<>) = unionWith (<>)
+  stimes n = fmap (stimes n)
+
+-- | 'unionWith'-based
+instance (Ord c, Semigroup a) => Monoid (TMap c a) where
+  mempty = empty
+  mappend = (<>)
+
+-- * Conversion
+
+toList :: TMap c a -> [([c], a)]
+toList = foldrWithKey (\k a r -> (k,a) : r) []
+
+fromList :: Ord c => [([c], a)] -> TMap c a
+fromList = List.foldl' (flip (uncurry insert)) empty
+
+toAscList :: TMap c a -> [([c], a)]
+toAscList = toList
+
+fromAscList :: Eq c => [([c], a)] -> TMap c a
+fromAscList [] = empty
+fromAscList [(cs, a)] = singleton cs a
+fromAscList pairs =
+  let (ma, gs) = group_ pairs
+      e = Map.fromDistinctAscList $ map (fmap fromAscList) gs
+  in TMap (Node ma e)
+
+group_ :: Eq c => [([c], a)] -> (Maybe a, [ (c, [ ([c], a) ]) ] )
+group_ = foldr step (Nothing, [])
+  where
+    step ([], a) (ma, gs) = (ma <|> Just a, gs)
+    step (c:cs, a) (ma, gs) = case gs of
+      (d,ps'):rest | c == d  -> (ma, (d, (cs,a):ps'):rest)
+      _            -> (ma, (c, [(cs,a)]):gs)
+
+toMap :: TMap c a -> Map [c] a
+toMap = Map.fromDistinctAscList . toAscList
+
+fromMap :: (Eq c) => Map [c] a -> TMap c a
+fromMap = fromAscList . Map.toAscList
+
+keysTSet :: TMap c a -> TSet c
+keysTSet = foldTMap keysTSet'
+  where
+    keysTSet' (Node ma e) =
+      TSet (TSet.Node (isJust ma) e)
+
+fromTSet :: ([c] -> a) -> TSet c -> TMap c a
+fromTSet f = go []
+  where
+    go q (TSet (TSet.Node a e)) =
+      let e' = Map.mapWithKey (\c -> go (c:q)) e
+          a' = if a then Just (f (reverse q)) else Nothing
+      in TMap (Node a' e')
+
+-- * Parsing
+
+toParser :: Alternative f =>
+     (c -> f c') -- ^ char
+  -> f eot       -- ^ eot
+  -> TMap c a -> f ([c'], a)
+toParser f eot = foldTMap toParser'
+  where
+    toParser' (Node ma e) =
+      maybe Ap.empty (\a -> ([], a) <$ eot) ma <|>
+      F.asum [ consFst <$> f c <*> p' | (c, p') <- Map.toAscList e ]
+
+    consFst c (cs, a) = (c:cs, a)
+
+toParser_ :: Alternative f =>
+     (c -> f c') -- ^ char
+  -> f eot       -- ^ eot
+  -> TMap c a -> f a
+toParser_ f eot = foldTMap toParser'
+  where
+    toParser' (Node ma e) =
+      maybe Ap.empty (<$ eot) ma <|>
+      F.asum [ f c *> p' | (c, p') <- Map.toAscList e ]
+
+toParser__ :: Alternative f =>
+     (c -> f c') -- ^ char
+  -> f eot       -- ^ eot
+  -> TMap c a -> f ()
+toParser__ f eot = void . toParser_ f eot
+
+-- * Traversing with keys
+
+-- | Same semantics to following defintion, but have
+--   more efficient implementation.
+--
+-- > traverseWithKey f = fmap fromAscList .
+-- >                     traverse (\(cs,a) -> (,) cs <$> f cs a) .
+-- >                     toAscList
+traverseWithKey :: (Applicative f) =>
+  ([c] -> a -> f b) -> TMap c a -> f (TMap c b)
+traverseWithKey f = go []
+  where
+    go q (TMap (Node ma e)) =
+      let step c = go (c : q)
+          e' = Map.traverseWithKey step e
+          mb = maybe (pure Nothing)
+                     (\a -> Just <$> f (reverse q) a)
+                     ma
+      in TMap <$> (Node <$> mb <*> e')
+
+-- | Same semantics to following defintion, but have
+--   more efficient implementation.
+--
+-- > traverseWithKey f = fromAscList .
+-- >                     map (\(cs,a) -> (cs,  f cs a)) .
+-- >                     toAscList
+mapWithKey :: ([c] -> a -> b) -> TMap c a -> TMap c b
+mapWithKey f = runIdentity . traverseWithKey (\k a -> Identity (f k a))
+
+-- | Same semantics to following defintion, but have
+--   more efficient implementation.
+--
+-- > foldMapWithKey f = foldMap (uncurry f) . toAscList
+foldMapWithKey :: (Monoid r) => ([c] -> a -> r) -> TMap c a -> r
+foldMapWithKey f = getConst . traverseWithKey (\k a -> Const (f k a))
+
+-- | Same semantics to following defintion, but have
+--   more efficient implementation.
+--
+-- > foldrWithKey f z = foldr (uncurry f) z . toAscList
+foldrWithKey :: ([c] -> a -> r -> r) -> r -> TMap c a -> r
+foldrWithKey f z (TMap (Node ma e)) =
+  case ma of
+    Nothing -> r
+    Just a  -> f [] a r
+  where
+    r = Map.foldrWithKey (\c subTrie s ->
+          foldrWithKey (f . (c:)) s subTrie) z e
+
+-- * Other operations
+
+foldTMap :: (Node c a r -> r) -> TMap c a -> r
+foldTMap f = go
+  where go (TMap node) = f (fmap go node)
diff --git a/src/Data/Trie/Map/Internal.hs b/src/Data/Trie/Map/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Map/Internal.hs
@@ -0,0 +1,39 @@
+{- |
+This module exposes internal representation of @TMap@.
+TMap has one invariant condition:
+
+* Subtrees of an @TMap@ should not be empty.
+
+For example, consider following tree structure which is valid:
+
+  > > fromList [("a",1), ("aa", 2), ("bc", 3)]
+  > Root
+  >   'a' -> 1
+  >     'a' -> 2
+  >   'b' ->
+  >     'c' -> 3
+
+Adding redundant node which represents empty map does not change
+what an @TMap@ represents.
+
+  > Root
+  >   'a' -> 1
+  >     'a' -> 2
+  >   'b' ->
+  >     'c' -> 3
+  >   'd' ->
+  >     'e' ->
+  >       'f' ->
+
+But such @TMap@ should not exist because it confuses @Eq@ and @Ord@
+instances and @null@ function.
+-}
+module Data.Trie.Map.Internal(
+  -- * Types
+  TMap(..),
+  Node(..),
+  foldTMap,
+)
+where
+
+import Data.Trie.Map.Hidden
diff --git a/src/Data/Trie/Set.hs b/src/Data/Trie/Set.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Set.hs
@@ -0,0 +1,50 @@
+{-|
+This module provides a type @TSet c@, which is a set of list of some
+characters. It serves almost same purpose to @Set [c]@, and functions of
+this module mirrors functions with same name from "Data.Set" module.
+
+The advantages to use this module over "Data.Set" are:
+
+* Faster 'member'.
+* Partial match provided by 'beginWith' function.
+* Efficient 'append', 'prefixes', and 'suffixes' functions.
+
+But notice for some disadvantages:
+
+* Some operations are slower than @Set [c]@. Especially, 'count' is much
+  much slower than 'Set.size' (because @Set.size@ is already recorded in the
+  data structure). Consider @TSet.count@ be like @length@ of list.
+* Constructed @TSet c@ from a list of lists @[[c]]@ do not share each member
+  lists with original list unlike @Set [c]@ does. This means holding both
+  @TSet c@ and @[[c]]@ in memory consumes much more memory than @Set [c]@ and
+  @[[c]]@.
+
+-}
+module Data.Trie.Set(
+  -- * Types
+  TSet(),
+  -- * Queries
+  member, notMember,
+  beginWith,
+  null, count, enumerate,
+  foldMap, foldr, foldl', 
+  -- * Construction
+  empty, epsilon,
+  singleton,
+  insert, delete,
+  -- * Combine
+  union, intersection, difference,
+  append,
+  -- * Other operations
+  prefixes, suffixes, infixes,
+  -- * Conversion
+  fromList, toList,
+  fromAscList, toAscList,
+  fromSet, toSet,
+  -- * Parsing
+  toParser, toParser_
+)
+where
+
+import Prelude hiding (foldr, foldMap, null)
+import Data.Trie.Set.Hidden
diff --git a/src/Data/Trie/Set/Hidden.hs b/src/Data/Trie/Set/Hidden.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Set/Hidden.hs
@@ -0,0 +1,304 @@
+{-# LANGUAGE DeriveTraversable #-}
+module Data.Trie.Set.Hidden(
+  -- * Types
+  TSet(..),
+  -- * Queries
+  member, notMember,
+  beginWith,
+  null, count, enumerate,
+  foldr, foldMap, foldl',
+  -- * Construction
+  empty, epsilon,
+  singleton,
+  insert, delete,
+  -- * Combine
+  union, intersection, difference,
+  append,
+  -- * Other operations
+  prefixes, suffixes, infixes,
+  -- * Conversion
+  fromList, toList,
+  fromAscList, toAscList,
+  fromSet, toSet,
+  -- * Parsing
+  toParser, toParser_,
+  -- * Low-level operation
+  Node(..),
+  foldTSet, paraTSet
+)
+where
+
+import Prelude hiding (foldMap, foldr, null)
+
+import           Control.Applicative hiding (empty)
+import qualified Control.Applicative as Ap
+
+import           Data.Semigroup
+import qualified Data.Foldable   as F
+import qualified Data.List       as List (foldr, foldl')
+import           Data.Maybe      (fromMaybe)
+import           Data.Map.Strict (Map)
+import qualified Data.Map.Strict as Map
+import           Data.Set        (Set)
+import qualified Data.Set        as Set
+import           Control.Arrow ((&&&))
+
+import Control.DeepSeq
+
+data Node c r = Node !Bool !(Map c r)
+  deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
+
+instance (NFData c, NFData r) => NFData (Node c r) where
+  rnf (Node a e) = rnf a `seq` rnf e
+
+newtype TSet c = TSet { getNode :: Node c (TSet c) }
+  deriving (Eq, Ord)
+
+instance Show c => Show (TSet c) where
+  showsPrec p t = showParen (p > 10) $
+    showString "fromList " . showsPrec 11 (enumerate t)
+
+instance (NFData c) => NFData (TSet c) where
+  rnf (TSet node) = rnf node
+
+{-
+
+The canonical Monoid instance could be (epsilon, append),
+but here I choose (empty, union) to align to Set instance.
+Semigroup instance must follow how Monoid is defined.
+
+-}
+
+-- | Semigroup(union)
+instance (Ord c) => Semigroup (TSet c) where
+  (<>) = union
+  stimes = stimesIdempotent
+
+-- | Monoid(empty, union)
+instance (Ord c) => Monoid (TSet c) where
+  mempty = empty
+  mappend = (<>)
+
+-- * Queries
+member :: (Ord c) => [c] -> TSet c -> Bool
+member [] (TSet (Node a _)) = a
+member (c:cs) (TSet (Node _ e)) =
+  case Map.lookup c e of
+    Nothing -> False
+    Just t' -> member cs t'
+
+notMember :: (Ord c) => [c] -> TSet c -> Bool
+notMember cs = not . member cs
+
+-- | @beginWith t xs@ returns new TSet @t'@ which contains
+--   all string @ys@ such that @t@ contains @xs ++ ys@.
+beginWith :: (Ord c) => TSet c -> [c] -> TSet c
+beginWith t       []               = t
+beginWith (TSet (Node _ e)) (c:cs) = 
+  case Map.lookup c e of
+    Nothing -> empty
+    Just t' -> beginWith t' cs
+
+null :: TSet c -> Bool
+null (TSet (Node a e)) = not a && Map.null e
+
+-- | Returns number of elements. @count@ takes O(number of nodes)
+--   unlike 'Set.size' which is O(1).
+count :: TSet c -> Int
+count = foldTSet count'
+  where
+    count' (Node a e) =
+      (if a then 1 else 0) + sum e
+
+-- | List of all elements.
+enumerate :: TSet c -> [[c]]
+enumerate = foldr (:) []
+
+{-
+from this post by u/foBrowsing:
+  https://www.reddit.com/r/haskell/comments/8krv31/how_to_traverse_a_trie/dzaktkn/
+-}
+foldr :: ([c] -> r -> r) -> r -> TSet c -> r
+foldr f z (TSet (Node a e))
+  | a         = f [] r
+  | otherwise = r
+  where
+    r = Map.foldrWithKey (\x tr xs -> foldr (f . (:) x) xs tr) z e
+
+foldMap :: (Monoid r) => ([c] -> r) -> TSet c -> r
+foldMap f (TSet (Node a e))
+  | a         = f [] `mappend` r
+  | otherwise = r
+  where
+    r = Map.foldMapWithKey (\c subTrie ->
+          foldMap (f . (c :)) subTrie) e
+
+foldl' :: (r -> [c] -> r) -> r -> TSet c -> r
+foldl' f z = List.foldl' f z . enumerate
+
+-- * Construction
+empty :: TSet c
+empty = TSet (Node False Map.empty)
+
+-- | @epsilon = singleton []@
+epsilon :: TSet c
+epsilon = TSet (Node True Map.empty)
+
+singleton :: [c] -> TSet c
+singleton = List.foldr cons epsilon
+
+cons :: c -> TSet c -> TSet c
+cons c t = TSet (Node False (Map.singleton c t))
+
+insert :: (Ord c, Foldable f) => f c -> TSet c -> TSet c
+insert = fst . F.foldr f (b, epsilon)
+  where
+    b (TSet (Node _ e)) = TSet (Node True e)
+    f x (inserter', xs') =
+      let inserter (TSet (Node a e)) =
+            let e' = Map.insertWith (const inserter') x xs' e
+            in TSet (Node a e')
+          xs = cons x xs'
+      in (inserter, xs)
+
+delete :: (Ord c, Foldable f) => f c -> TSet c -> TSet c
+delete cs t = fromMaybe empty $ delete_ cs t
+
+delete_ :: (Ord c, Foldable f) => f c -> TSet c -> Maybe (TSet c)
+delete_ = F.foldr f b
+  where
+    b (TSet (Node _ e)) =
+      if Map.null e then Nothing else Just (TSet (Node False e))
+    f x xs (TSet (Node a e)) =
+      let e' = Map.update xs x e
+          t' = TSet (Node a e')
+      in if null t' then Nothing else Just t'
+
+-- * Combine
+union :: (Ord c) => TSet c -> TSet c -> TSet c
+union (TSet (Node ax ex)) (TSet (Node ay ey)) = TSet (Node az ez)
+  where
+    az = ax || ay
+    ez = Map.unionWith union ex ey
+
+intersection :: (Ord c) => TSet c -> TSet c -> TSet c
+intersection x y = fromMaybe empty $ intersection_ x y
+
+intersection_ :: (Ord c) => TSet c -> TSet c -> Maybe (TSet c)
+intersection_ (TSet (Node ax ex)) (TSet (Node ay ey)) =
+    if not az && Map.null ez
+      then Nothing
+      else Just $ TSet (Node az ez)
+  where
+    az = ax && ay
+    emz = Map.intersectionWith intersection_ ex ey
+    ez = Map.mapMaybe id emz
+
+difference :: (Ord c) => TSet c -> TSet c -> TSet c
+difference x y = fromMaybe empty $ difference_ x y
+
+difference_ :: (Ord c) => TSet c -> TSet c -> Maybe (TSet c)
+difference_ (TSet (Node ax ex)) (TSet (Node ay ey)) =
+    if not az && Map.null ez
+      then Nothing
+      else Just $ TSet (Node az ez)
+  where
+    az = ax > ay
+    ez = Map.differenceWith difference_ ex ey
+
+append :: (Ord c) => TSet c -> TSet c -> TSet c
+append _ y | null y = empty
+append (TSet (Node ax ex)) y = f (TSet (Node False ez))
+  where
+    ez = Map.map (`append` y) ex
+    f = if ax then union y else id
+
+-- * Other operations
+
+prefixes :: TSet c -> TSet c
+prefixes t | null t    = empty
+           | otherwise = foldTSet prefixes' t
+  where
+    prefixes' (Node _ e) = TSet (Node True e)
+
+suffixes :: (Ord c) => TSet c -> TSet c
+suffixes = paraTSet suffixes'
+  where
+    suffixes' nx = union (TSet (fst <$> nx)) (F.foldMap snd nx)
+
+infixes :: (Ord c) => TSet c -> TSet c
+infixes = suffixes . prefixes
+
+-- * Conversion
+toList, toAscList :: TSet c -> [[c]]
+toList = enumerate
+toAscList = enumerate
+
+fromList :: (Ord c) => [[c]] -> TSet c
+fromList = List.foldl' (flip insert) empty
+
+fromAscList :: (Eq c) => [[c]] -> TSet c
+fromAscList [] = empty
+fromAscList [cs] = singleton cs
+fromAscList xs =
+  let (a,es) = groupStrs xs
+      e' = Map.fromDistinctAscList $ map (fmap fromAscList) es
+  in TSet (Node a e')
+
+groupStrs :: (Eq c) => [[c]] -> (Bool, [(c,[[c]])])
+groupStrs = List.foldr pushStr (False, [])
+  where
+    pushStr [] (_, gs) = (True, gs)
+    pushStr (c:cs) (hasNull, gs) =
+      case gs of
+        (d, dss):rest | c == d -> (hasNull, (d, cs:dss):rest)
+        _                      -> (hasNull, (c, [cs]):gs)
+
+toSet :: TSet c -> Set [c]
+toSet = Set.fromDistinctAscList . enumerate
+
+fromSet :: (Eq c) => Set [c] -> TSet c
+fromSet = fromAscList . Set.toAscList
+
+-- * Parsing
+
+-- | Construct a \"parser\" which recognizes member strings
+--   of a TSet.
+--
+--   * @char@ constructs a parser which recognizes a character.
+--   * @eot@ recognizes the end of a token.
+toParser :: (Alternative f) =>
+  (c -> f a) -- ^ char
+  -> f b     -- ^ eot
+  -> TSet c -> f [a]
+toParser char eot = foldTSet enumerateA'
+  where
+    enumerateA' (Node a e) =
+      (if a then [] <$ eot else Ap.empty) <|>
+      F.asum [ (:) <$> char c <*> as | (c, as) <- Map.toAscList e ]
+
+-- | Construct a \"parser\" which recognizes member strings
+--   of a TSet.
+--   It discards the information which string it is recognizing.
+--
+--   * @char@ constructs a parser which recognizes a character.
+--   * @eot@ recognizes the end of a token.
+toParser_ :: (Alternative f) =>
+  (c -> f a) -- ^ char
+  -> f b     -- ^ eot
+  -> TSet c -> f ()
+toParser_ char eot = foldTSet enumerateA'
+  where
+    enumerateA' (Node a e) =
+      (if a then () <$ eot else Ap.empty) <|>
+      F.asum [ char c *> as | (c, as) <- Map.toAscList e ]
+
+----------------------
+
+foldTSet :: (Node c r -> r) -> TSet c -> r
+foldTSet f = go
+  where go (TSet node) = f (fmap go node)
+
+paraTSet :: (Node c (TSet c, r) -> r) -> TSet c -> r
+paraTSet f = go
+  where go (TSet node) = f (fmap (id &&& go) node)
diff --git a/src/Data/Trie/Set/Internal.hs b/src/Data/Trie/Set/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Trie/Set/Internal.hs
@@ -0,0 +1,37 @@
+{- |
+This module exposes internal representation of @TSet@.
+TSet has one invariant condition:
+
+* Subtrees of an @TSet@ should not be empty.
+
+For example, consider following tree structure which is valid:
+
+  > > fromList ["a", "aa", "bc"]
+  > Root -> False
+  >   'a' -> True
+  >     'a' -> True
+  >   'b' -> False
+  >     'c' -> True
+
+Adding redundant node which represents empty set does not change
+what an @TSet@ represents.
+
+  > Root -> False
+  >   'a' -> True
+  >     'a' -> True
+  >   'b' -> False
+  >     'c' -> True
+  >   'd' -> False
+  >     'e' -> False
+  >       'f' -> False
+
+But such @TSet@ should not exist because it confuses @Eq@ and @Ord@ instances and @null@ function.
+-}
+module Data.Trie.Set.Internal(
+  TSet(..),
+  Node(..),
+  foldTSet, paraTSet
+)
+where
+
+import Data.Trie.Set.Hidden
diff --git a/test/Common.hs b/test/Common.hs
new file mode 100644
--- /dev/null
+++ b/test/Common.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE DeriveFunctor #-}
+module Common where
+
+import           Test.QuickCheck
+
+import           Control.Applicative
+import           Data.List          (tails)
+import           Data.Map           (Map)
+import qualified Data.Map.Strict as Map
+
+-- Alphabet-only Char
+newtype C = C { unC :: Char }
+  deriving (Eq, Ord)
+
+instance Show C where
+  showsPrec p (C c) = showsPrec p c
+  showList cs = showList (map unC cs)
+
+instance Arbitrary C where
+  arbitrary = sized $ \n ->
+      let m = sqrti (max 1 n)
+      in C <$> elements (take m "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+-- Char which is either '0' or '1', always
+newtype B = B { unB :: Char }
+  deriving (Eq, Ord)
+
+instance Show B where
+    showsPrec p (B b) = showsPrec p b
+    showList bs = showList (map unB bs)
+
+instance Arbitrary B where
+  -- Distribution is biased
+  -- (To increase probability of randomly generated two [B]
+  --  coincide.)
+  arbitrary = B <$> elements "00001"
+
+sqrti :: Int -> Int
+sqrti = floor . (sqrt :: Double -> Double) . fromIntegral
+
+randomPair :: [a] -> Gen (a,a)
+randomPair as =
+  let mkP [] = []
+      mkP [_] = []
+      mkP (x:xs) = (,) x <$> xs
+      ps = tails as >>= mkP
+  in elements ps
+
+data Once a = Once { getDefault :: a, getVariants :: [a] }
+  deriving (Functor)
+
+instance Applicative Once where
+    pure a = Once a []
+    Once f fs <*> Once a as = Once (f a) (fs <*> pure a <|> f <$> as)
+
+shrinkTraversableBy :: (Traversable t) => (a -> [a]) -> t a -> [t a]
+shrinkTraversableBy f = getVariants . traverse (\a -> Once a (f a))
+
+shrinkMapBy :: (Ord k) => (a -> [a]) -> Map k a -> [Map k a]
+shrinkMapBy f = map Map.fromList . shrinkList f' . Map.toList
+  where f' (k, a) = (,) k <$> f a
diff --git a/test/Data/Trie/Map/Gen.hs b/test/Data/Trie/Map/Gen.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Trie/Map/Gen.hs
@@ -0,0 +1,51 @@
+module Data.Trie.Map.Gen(
+  C(..),
+  TMap'(..),
+  TMap''(..),
+  genTMap,
+  validTMap
+) where
+
+import           Test.QuickCheck hiding (shrinkMapBy)
+
+import           Data.Maybe
+import qualified Data.Map as Map
+
+import           Data.Trie.Map
+import           Data.Trie.Map.Internal
+import           Common
+
+newtype TMap' = TMap' (TMap C Int)
+
+instance Show TMap' where
+  show (TMap' t) = show t
+
+instance Arbitrary TMap' where
+  arbitrary = TMap' <$> genTMap 
+  shrink (TMap' t) = TMap' <$> shrinkTMap t
+
+newtype TMap'' = TMap'' (TMap B Int)
+
+instance Show TMap'' where
+  show (TMap'' t) = show t
+
+instance Arbitrary TMap'' where
+  arbitrary = TMap'' <$> genTMap 
+  shrink (TMap'' t) = TMap'' <$> shrinkTMap t
+
+genTMap :: (Ord c, Arbitrary c, Arbitrary a) => Gen (TMap c a)
+genTMap = fromList <$> arbitrary
+
+shrinkTMap :: (Ord c, Arbitrary c, Arbitrary a) => TMap c a -> [TMap c a]
+shrinkTMap (TMap (Node ma e)) = filter validTMap $ 
+  [ TMap (Node Nothing e) | Just _ <- [ma] ] ++
+  [ TMap (Node (Just a') e) | Just a <- [ma], a' <- shrink a ] ++
+  [ TMap (Node ma e') | e' <- shrinkMapBy shrinkTMap e ]
+
+validTMap :: TMap c a -> Bool
+validTMap = snd . foldTMap step
+  where
+    step (Node ma e) =
+      let isEmpty = isNothing ma && all fst e
+          isValid = (not isEmpty || Map.null e) && all snd e
+      in (isEmpty, isValid)
diff --git a/test/Data/Trie/MapSpec.hs b/test/Data/Trie/MapSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Trie/MapSpec.hs
@@ -0,0 +1,124 @@
+module Data.Trie.MapSpec(
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Data.List (sortBy, foldl')
+import           Data.Ord
+import           Data.Map            (Map)
+import qualified Data.Map            as Map
+import           Data.Semigroup
+
+import           Data.Trie.Map     as T
+import           Data.Trie.Map.Gen
+import qualified Data.Trie.Set     as TSet
+import           Data.Trie.Set.Gen
+
+spec :: Spec
+spec = do
+  specify "toList empty = []" $
+    toList (empty :: TMap C Int) `shouldSatisfy` Prelude.null
+  specify "toList (just a) = [([], a)]" $
+    property $ \a -> toList (just a :: TMap C Int) === [([], a)]
+  specify "toList (singleton k a) = [(k,a)]" $
+    property $ \k a -> toList (singleton k a :: TMap C Int) === [(k,a)]
+  specify "toAscList . fromList = Map.toAscList . Map.fromList" $
+    property $ \strs -> toAscList (fromList strs :: TMap C Int) ==
+                        (Map.toAscList . Map.fromList) strs
+  specify "fromAscList . sortBy (comparing fst) = fromList" $
+    property $ \strs -> fromAscList (sortBy (comparing fst) strs) ==
+                        (fromList strs :: TMap C Int)
+  specify "fromMap . Map.fromList = fromList" $
+    property $ \strs -> fromMap (Map.fromList strs) ==
+                        (fromList strs :: TMap C Int)
+  specify "null . toList = null" $
+    property $ \(TMap' t) -> Prelude.null (toList t) === T.null t
+  specify "length . toList = count" $
+    property $ \(TMap' t) -> length (toList t) === count t
+  specify "keys = map fst . toList" $
+    property $ \(TMap' t) -> keys t == map fst (toList t)
+  specify "elems = map snd . toList" $
+    property $ \(TMap' t) -> elems t == map snd (toList t)
+  specify "keysTSet = TSet.fromAscList . keys" $
+    property $ \(TMap' t) -> keysTSet t == TSet.fromAscList (keys t)
+  specify "fromTSet id = fromAscList . map (\\k -> (k,k)) . TSet.toAscList" $
+    property $ \(TSet' t) ->
+      fromTSet id t == fromAscList (map (\k -> (k,k)) (TSet.toAscList t))
+  
+  specify "member k t = (k `Map.member` toMap t)" $
+    property $ \(TMap'' t) ->
+      let strMap = toMap t
+      in property $ \str -> member str t == Map.member str strMap
+  specify "lookup k t = (k `Map.lookup` toMap t)" $
+    property $ \(TMap'' t) ->
+      let strMap = toMap t
+      in property $ \str -> T.lookup str t == Map.lookup str strMap
+  specify "lookup (k ++ l) t == lookup l (snd (match k t))" $
+    property $ \k l (TMap'' t) ->
+      T.lookup (k ++ l) t == T.lookup l (snd (T.match k t))
+
+  specify "toMap (insert k 1 a) = Map.insert k 1 (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (insert k 1 a) == Map.insert k 1 (toMap a)
+  specify "toMap (delete k a) = Map.delete k (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (delete k a) == Map.delete k (toMap a)
+  specify "toMap (insertWith (+) k 1 a) = Map.insertWith (+) k 1 (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (insertWith (+) k 1 a) ==
+      Map.insertWith (+) k 1 (toMap a)
+  let sub b a = if a < b then Nothing else Just (a - b)
+  specify "toMap (deleteWith sub k 1 a) = Map.update (sub 1) k (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (deleteWith sub k 1 a) ==
+      Map.update (sub 1) k (toMap a)
+  specify "toMap (adjust (*2) k a) = Map.adjust (*2) k (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (adjust (*2) k a) == Map.adjust (*2) k (toMap a)
+  let alterer Nothing = Just 100
+      alterer (Just a) | odd a = Nothing
+                       | otherwise = Just (a `div` 2)
+  specify "toMap (alter alterer k a) = Map.alter alterer k (toMap a)" $
+    property $ \k (TMap'' a) ->
+      toMap (alter alterer k a) == Map.alter alterer k (toMap a)
+
+  specify "toMap (union a b) = Map.union (toMap a) (toMap b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      toMap (union a b) == Map.union (toMap a) (toMap b)
+  specify "toMap (unionWith f a b) = Map.unionWith f (toMap a) (toMap b)" $
+    property $ \(Fn2 f) (TMap' a) (TMap' b) ->
+      toMap (unionWith f a b) == Map.unionWith f (toMap a) (toMap b)
+  specify "toMap (intersection a b) = Map.intersection (toMap a) (toMap b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      toMap (intersection a b) == Map.intersection (toMap a) (toMap b)
+  specify "toMap (difference a b) = Map.difference (toMap a) (toMap b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      toMap (difference a b) == Map.difference (toMap a) (toMap b)
+  specify "toMap (append a b) = mapAppend (toMap a) (toMap b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      toMap (getSum <$> appendWith (\x y -> Sum (x * y)) a b) ==
+      mapAppend (toMap a) (toMap b)
+      
+  specify "validTMap (union a b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      validTMap (union a b)
+  specify "validTMap (intersection a b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      validTMap (intersection a b)
+  specify "validTMap (difference a b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      validTMap (difference a b)
+  specify "validTMap (append a b)" $
+    property $ \(TMap' a) (TMap' b) ->
+      validTMap (getSum <$> appendWith (\x y -> Sum (x * y)) a b)
+
+
+mapAppend :: (Ord c) => Map [c] Int -> Map [c] Int -> Map [c] Int
+mapAppend ass bss =
+  sumUnions
+    [ Map.mapKeysMonotonic (as ++) $ Map.map (v *) bss
+      | (as, v) <- Map.toAscList ass ]
+  where
+    sumUnions = foldl' (Map.unionWith (+)) Map.empty
diff --git a/test/Data/Trie/Set/Gen.hs b/test/Data/Trie/Set/Gen.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Trie/Set/Gen.hs
@@ -0,0 +1,58 @@
+module Data.Trie.Set.Gen(
+  C(..),
+  TSet'(..),
+  TSet''(..),
+  genTSet,
+  acceptStrs,
+  validTSet
+) where
+
+import           Test.QuickCheck hiding (shrinkMapBy)
+
+import qualified Data.Map as Map
+import           Data.Trie.Set
+import           Data.Trie.Set.Internal
+import           Common
+
+newtype TSet' = TSet' (TSet C)
+newtype TSet'' = TSet'' (TSet B)
+
+instance Show TSet' where
+  show (TSet' t) = show t
+
+instance Arbitrary TSet' where
+  arbitrary = TSet' <$> genTSet 
+  shrink (TSet' t) = TSet' <$> shrinkTSet t
+
+instance Show TSet'' where
+  show (TSet'' t) = show t
+
+instance Arbitrary TSet'' where
+  arbitrary = TSet'' <$> genTSet 
+  shrink (TSet'' t) = TSet'' <$> shrinkTSet t
+
+genTSet :: (Ord c, Arbitrary c) => Gen (TSet c)
+genTSet = fromList <$> arbitrary
+
+shrinkTSet :: (Ord c, Arbitrary c) => TSet c -> [TSet c]
+shrinkTSet (TSet (Node a e)) = filter validTSet $
+  [ TSet (Node False e) | a ] ++
+  [ TSet (Node a e') | e' <- shrinkMapBy shrinkTSet e ]
+
+acceptStrs :: TSet c -> Gen [[c]]
+acceptStrs t = sized $ \n ->
+  let m = count t
+      loop _ [] = return []
+      loop k (a:as)
+        | k <= 0    = return []
+        | otherwise =
+            frequency [(n, (a:) <$> loop (k-1) as), (m, loop k as)]
+  in loop n (enumerate t)
+
+validTSet :: TSet c -> Bool
+validTSet = snd . foldTSet step
+  where
+    step (Node a e) =
+      let isEmpty = not a && all fst e
+          isValid = (not isEmpty || Map.null e) && all snd e
+      in (isEmpty, isValid)
diff --git a/test/Data/Trie/SetSpec.hs b/test/Data/Trie/SetSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Data/Trie/SetSpec.hs
@@ -0,0 +1,94 @@
+module Data.Trie.SetSpec(
+  spec
+) where
+
+import           Test.Hspec
+import           Test.QuickCheck
+
+import           Data.List         (sort, inits, tails)
+import           Data.Set          (Set)
+import qualified Data.Set          as Set
+
+import           Data.Trie.Set     as T
+import           Data.Trie.Set.Gen
+
+spec :: Spec
+spec = do
+  specify "enumerate empty = []" $
+    enumerate (empty :: TSet C) `shouldSatisfy` Prelude.null
+  specify "enumerate epsilon = [[]]" $
+    enumerate (epsilon :: TSet C) `shouldBe` [[]]
+  specify "enumerate . singleton = (:[])" $
+    property $ \str -> enumerate (singleton (str :: [C])) === [str]
+  specify "enumerate . fromList = Set.toAscList . Set.fromList" $
+    property $ \strs -> (enumerate . fromList) (strs :: [[C]]) == (Set.toAscList . Set.fromList) strs
+  specify "null . enumerate = null" $
+    property $ \(TSet' t) -> Prelude.null (enumerate t) === T.null t
+  specify "length . enumerate = count" $
+    property $ \(TSet' t) -> length (enumerate t) === count t
+
+  specify "toSet (insert xs a) = Set.insert xs (toSet a)" $
+    property $ \xs (TSet' t) -> toSet (T.insert xs t) == Set.insert xs (toSet t)
+  specify "toSet (delete xs a) = Set.delete xs (toSet a)" $
+    property $ \xs (TSet' t) -> toSet (T.delete xs t) == Set.delete xs (toSet t)
+  
+  specify "member t = (`Set.member` toSet t)" $
+    property $ \(TSet'' t) ->
+      let strSet = toSet t
+      in property $ \str -> member str t == Set.member str strSet
+  specify "forAll (str `in` enumerate t). member str t" $
+    property $ \(TSet'' t) -> all (`member` t) <$> acceptStrs t
+  specify "member (xs ++ ys) t = member ys (beginWith t xs)" $
+    property $ \xs ys (TSet'' t) ->
+      member (xs ++ ys) t == member ys (beginWith t xs)
+
+  specify "toSet (union a b) = Set.union (toSet a) (toSet b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      toSet (union a b) == Set.union (toSet a) (toSet b)
+  specify "toSet (intersection a b) = Set.intersection (toSet a) (toSet b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      toSet (intersection a b) == Set.intersection (toSet a) (toSet b)
+  specify "toSet (difference a b) = Set.difference (toSet a) (toSet b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      toSet (difference a b) == Set.difference (toSet a) (toSet b)
+  specify "toSet (append a b) = setAppend (toSet a) (toSet b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      toSet (append a b) == setAppend (toSet a) (toSet b)
+
+  specify "validTSet (union a b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      validTSet (union a b)
+  specify "validTSet (intersection a b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      validTSet (intersection a b)
+  specify "validTSet (difference a b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      validTSet (difference a b)
+  specify "validTSet (append a b)" $
+    property $ \(TSet' a) (TSet' b) ->
+      validTSet (append a b)
+  
+  specify "toSet (prefixes a) = setPrefixes (toSet a)" $
+    property $ \(TSet' a) ->
+      toSet (prefixes a) === setPrefixes (toSet a)
+  specify "toSet (suffixes a) = setSuffixes (toSet a)" $
+    property $ \(TSet' a) ->
+      toSet (suffixes a) === setSuffixes (toSet a)
+
+  specify "fromAscList . sort = fromList" $
+    property $ \strs -> fromAscList (sort strs) == fromList (strs :: [[C]])
+  specify "fromSet . Set.fromList = fromList" $
+    property $ \strs -> fromSet (Set.fromList strs) == fromList (strs :: [[C]])
+
+setAppend :: (Ord c) => Set [c] -> Set [c] -> Set [c]
+setAppend ass bss = Set.unions
+  [ Set.mapMonotonic (as ++) bss
+      | as <- Set.toAscList ass ]
+
+setPrefixes :: (Ord c) => Set [c] -> Set [c]
+setPrefixes ass = Set.unions
+  [ Set.fromDistinctAscList (inits as) | as <- Set.toAscList ass ]
+
+setSuffixes :: (Ord c) => Set [c] -> Set [c]
+setSuffixes ass = Set.fromList
+  [ bs | as <- Set.toAscList ass, bs <- tails as ]
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
diff --git a/trie-simple.cabal b/trie-simple.cabal
new file mode 100644
--- /dev/null
+++ b/trie-simple.cabal
@@ -0,0 +1,73 @@
+name:                trie-simple
+version:             0.4.1.1
+synopsis:            Simple Map-based Trie
+description:
+  Trie data structure `TMap` to hold mapping from list of characters to
+  something, i.e. isomorphic to `Map [c] v`.
+  
+  It is more efficient to query compared to `Map`. Also, it supports extra
+  operation like prefix matching.
+  This package also contains `TSet`, which is isomorphic to `Set` of lists of
+  characters.
+license:             BSD3
+license-file:        LICENSE
+author:              Koji Miyazato
+maintainer:          viercc@gmail.com
+copyright:           Koji Miyazato
+category:            Data Structure
+build-type:          Simple
+extra-source-files:  README.md, CHANGELOG.md
+cabal-version:       >=1.10
+
+source-repository head
+  type:     git
+  location: https://github.com/viercc/trie-simple
+  branch:   master
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Data.Trie.Set,
+                       Data.Trie.Set.Internal,
+                       Data.Trie.Map,
+                       Data.Trie.Map.Internal
+  other-modules:       Data.Trie.Set.Hidden,
+                       Data.Trie.Map.Hidden
+  build-depends:       base         >= 4.9     && < 4.13,
+                       containers   >= 0.5.7.1 && < 0.6,
+                       deepseq      >= 1.4.2.0 && < 1.5,
+                       mtl          >= 2.2.1   && < 2.3
+  default-language:    Haskell2010
+  ghc-options:         -Wall -Wno-dodgy-exports
+
+test-suite trie-simple-test
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: test
+  main-is:        Spec.hs
+  other-modules:  Data.Trie.SetSpec,
+                  Data.Trie.Set.Gen,
+                  Data.Trie.MapSpec,
+                  Data.Trie.Map.Gen,
+                  Common
+  build-depends: base,
+                 containers,
+                 vector,
+                 QuickCheck,
+                 hspec,
+                 trie-simple
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  default-language: Haskell2010
+
+benchmark trie-benchmark
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      bench
+  main-is:             trie-benchmark.hs
+  other-modules:       Common
+  build-depends:       base,
+                       deepseq,
+                       containers,
+                       criterion,
+                       vector,
+                       mwc-random,
+                       trie-simple
+  ghc-options:         -O2 -Wall -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
