diff --git a/executables/ConcurrentInsertionBench.hs b/executables/ConcurrentInsertionBench.hs
--- a/executables/ConcurrentInsertionBench.hs
+++ b/executables/ConcurrentInsertionBench.hs
@@ -8,31 +8,33 @@
 import qualified Control.Concurrent.Async as Async
 import qualified System.Random.MWC.Monad as MWC
 import qualified Focus
+import qualified Data.Char as Char
+import qualified Data.Text as Text
 
 
 type UCMap k v = TVar (UC.HashMap k (TVar v))
 
 
--- * Actions 
+-- * Transactions 
 -------------------------
 
-data ActionF k v n where
-  Insert :: k -> v -> n -> ActionF k v n
+data TransactionF k v n where
+  Insert :: v -> k -> n -> TransactionF k v n
   deriving (Functor)
 
-type Action k v = Free (ActionF k v)
+type Transaction k v = Free (TransactionF k v)
 
 
 -- * Interpreters
 -------------------------
 
 type Interpreter m = 
-  forall k v r. (Hashable k, Eq k) => m k v -> Action k v r -> STM r
+  forall k v r. (Hashable k, Eq k) => m k v -> Transaction k v r -> STM r
 
 ucInterpreter :: Interpreter UCMap
 ucInterpreter m = 
   iterM $ \case
-    Insert k v n -> do
+    Insert v k n -> do
       mv <- readTVar m
       vt <- newTVar v
       writeTVar m $! UC.insert k vt mv
@@ -41,33 +43,33 @@
 specializedSCInterpreter :: Interpreter SC.Map
 specializedSCInterpreter m =
   iterM $ \case
-    Insert k v n -> SC.insert v k m >> n
+    Insert v k n -> SC.insert v k m >> n
 
 focusSCInterpreter :: Interpreter SC.Map
 focusSCInterpreter m =
   iterM $ \case
-    Insert k v n -> SC.focus (Focus.insertM v) k m >> n
+    Insert v k n -> SC.focus (Focus.insertM v) k m >> n
 
 
 -- * Session and runners
 -------------------------
 
 -- | A list of transactions per thread.
-type Session k v = [[Action k v ()]]
+type Session k v = [[Transaction k v ()]]
 
 type SessionRunner = 
   forall k v. (Hashable k, Eq k) => Session k v -> IO ()
 
 scSessionRunner :: Interpreter SC.Map -> SessionRunner
-scSessionRunner interpreter threadActions = do
+scSessionRunner interpreter threadTransactions = do
   m <- atomically $ SC.new
-  void $ flip Async.mapConcurrently threadActions $ \actions -> do
+  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
     forM_ actions $ atomically . interpreter m
 
 ucSessionRunner :: SessionRunner
-ucSessionRunner threadActions = do
+ucSessionRunner threadTransactions = do
   m <- newTVarIO UC.empty
-  void $ flip Async.mapConcurrently threadActions $ \actions -> do
+  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
     forM_ actions $ atomically . ucInterpreter m
 
 
@@ -76,13 +78,19 @@
 
 type Generator a = MWC.Rand IO a
 
-transactionGenerator :: Generator (Action Float Int ())
-transactionGenerator =
-  Free <$> (Insert <$> k <*> v <*> n)
+transactionGenerator :: Generator (Transaction Text.Text () ())
+transactionGenerator = do
+  k <- keyGenerator
+  return $ Free $ Insert () k (Pure ())
+
+keyGenerator :: Generator Text.Text
+keyGenerator = do
+  l <- length
+  s <- replicateM l char
+  return $! Text.pack s
   where
-    k = MWC.uniform
-    v = MWC.uniform
-    n = MWC.uniform >>= bool (return (Pure ())) transactionGenerator
+    length = MWC.uniformR (7, 20)
+    char = Char.chr <$> MWC.uniformR (Char.ord 'a', Char.ord 'z')
 
 
 -- * Utils
@@ -99,11 +107,11 @@
 -------------------------
 
 main = do
-  allActions <- MWC.runWithCreate $ replicateM actionsNum transactionGenerator
+  allTransactions <- MWC.runWithCreate $ replicateM actionsNum transactionGenerator
   defaultMain $! flip map threadsNums $! \threadsNum ->
     let
       sliceSize = actionsNum `div` threadsNum
-      threadActions = slices sliceSize allActions
+      threadTransactions = slices sliceSize allTransactions
       in 
         bgroup
           (shows threadsNum . showString "/" . shows sliceSize $ "")
@@ -111,13 +119,13 @@
             bgroup "STM Containers"
               [
                 bench "Focus-based" $ 
-                  scSessionRunner focusSCInterpreter threadActions,
+                  scSessionRunner focusSCInterpreter threadTransactions,
                 bench "Specialized" $ 
-                  scSessionRunner specializedSCInterpreter threadActions
+                  scSessionRunner specializedSCInterpreter threadTransactions
               ],
             bench "Unordered Containers" $
-              ucSessionRunner threadActions
+              ucSessionRunner threadTransactions
           ]
   where
     actionsNum = 100000
-    threadsNums = [1, 2, 4, 8, 16, 32]
+    threadsNums = [1, 2, 4, 6, 8, 12, 16, 32]
diff --git a/executables/ConcurrentTransactionsBench.hs b/executables/ConcurrentTransactionsBench.hs
new file mode 100644
--- /dev/null
+++ b/executables/ConcurrentTransactionsBench.hs
@@ -0,0 +1,208 @@
+
+import STMContainers.Prelude
+import STMContainers.Transformers
+import Criterion.Main
+import Control.Monad.Free
+import Control.Monad.Free.TH
+import qualified Data.HashMap.Strict as UC
+import qualified STMContainers.Map as SC
+import qualified Control.Concurrent.Async as Async
+import qualified System.Random.MWC.Monad as MWC
+import qualified Focus
+import qualified Data.Char as Char
+import qualified Data.Text as Text
+import qualified Data.Set as Set
+
+
+-- * Custom data structures
+-------------------------
+
+type UCMap k v = TVar (UC.HashMap k (TVar v))
+
+
+-- * Transactions 
+-------------------------
+
+data TransactionF k v n where
+  Insert :: v -> k -> n -> TransactionF k v n
+  Delete :: k -> n -> TransactionF k v n
+  Lookup :: k -> n -> TransactionF k v n
+  deriving (Functor, Show)
+
+type Transaction k v = Free (TransactionF k v)
+
+-- * Interpreters
+-------------------------
+
+type Interpreter m = 
+  forall k v r. (Hashable k, Eq k) => m k v -> Transaction k v r -> STM r
+
+ucInterpreter :: Interpreter UCMap
+ucInterpreter m = 
+  iterM $ \case
+    Insert v k c -> 
+      do
+        mv <- readTVar m
+        vt <- newTVar v
+        writeTVar m $! UC.insert k vt mv
+        c
+    Delete k c -> 
+      readTVar m >>= writeTVar m . UC.delete k >> c
+    Lookup k c -> 
+      readTVar m >>= mapM readTVar . UC.lookup k >> c
+
+specializedSCInterpreter :: Interpreter SC.Map
+specializedSCInterpreter m =
+  iterM $ \case
+    Insert v k c -> SC.insert v k m >> c
+    Delete k c   -> SC.delete k m >> c
+    Lookup k c   -> SC.lookup k m >> c
+
+focusSCInterpreter :: Interpreter SC.Map
+focusSCInterpreter m =
+  iterM $ \case
+    Insert v k c -> SC.focus (Focus.insertM v) k m >> c
+    Delete k c   -> SC.focus Focus.deleteM k m >> c
+    Lookup k c   -> SC.focus Focus.lookupM k m >> c
+
+
+-- * Session and runners
+-------------------------
+
+-- | A list of transactions per thread.
+type Session k v = [[Transaction k v ()]]
+
+type SessionRunner = 
+  forall k v. (Hashable k, Eq k) => Session k v -> IO ()
+
+scSessionRunner :: Interpreter SC.Map -> SessionRunner
+scSessionRunner interpreter threadTransactions = do
+  m <- atomically $ SC.new
+  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
+    forM_ actions $ atomically . interpreter m
+
+ucSessionRunner :: SessionRunner
+ucSessionRunner threadTransactions = do
+  m <- newTVarIO UC.empty
+  void $ flip Async.mapConcurrently threadTransactions $ \actions -> do
+    forM_ actions $ atomically . ucInterpreter m
+
+
+-- * Generators
+-------------------------
+
+type Generator a = MWC.Rand IO a
+
+-- |
+-- Generate a list of transactions with a context of shared keys.
+transactionsGroupGenerator :: Int -> Generator [Transaction Text.Text () ()]
+transactionsGroupGenerator n =
+  (flip evalStateT) Set.empty $ replicateM n transaction
+  where
+    transaction = do
+      s <- size
+      fmap sequence_ $ replicateM s action
+      where
+        size = 
+          lift $ join $ weightedElementGenerator $
+          [
+            (1  , MWC.uniformR (8, 14)),
+            (5  , MWC.uniformR (2, 7)),
+            (10 , return 1)
+          ]
+        action =
+          join $ lift $ weightedElementGenerator $
+          [
+            (1, delete),
+            (5, insert),
+            (20, lookup)
+          ]
+          where
+            insert = do
+              k <- key 10 1
+              modify $ Set.insert k
+              return $ liftF (Insert () k ())
+            delete = do
+              k <- key 1 10
+              modify $ Set.delete k
+              return $ liftF (Delete k ())
+            lookup = do
+              k <- key 1 5
+              return $ liftF (Lookup k ())
+            key unknownWeight knownWeight =
+              join $ lift $ weightedElementGenerator $ 
+              [
+                (unknownWeight, unknown), 
+                (knownWeight, known)
+              ]
+              where
+                unknown = lift $ keyGenerator
+                known = do
+                  allKeys <- get
+                  maybe unknown return =<< lift (setElementGenerator allKeys)
+              
+keyGenerator :: Generator Text.Text
+keyGenerator = do
+  l <- length
+  s <- replicateM l char
+  return $! Text.pack s
+  where
+    length = MWC.uniformR (7, 20)
+    char = Char.chr <$> MWC.uniformR (Char.ord 'a', Char.ord 'z')
+
+setElementGenerator :: Set.Set a -> Generator (Maybe a)
+setElementGenerator set = do
+  case Set.size set of
+    0 -> return Nothing
+    size -> Just . (flip Set.elemAt) set <$> MWC.uniformR (0, pred size)
+
+elementGenerator :: [a] -> Generator a
+elementGenerator = \case
+  [] -> $bug "Empty list"
+  l -> (!!) l <$> MWC.uniformR (0, (pred . length) l)
+
+weightedElementGenerator :: [(Int, a)] -> Generator a
+weightedElementGenerator = \case
+  [] -> 
+    $bug "Empty list"
+  l -> 
+    (flip pick) l <$> MWC.uniformR (1, total)
+    where
+      total = (sum . map fst) l
+      pick n = \case
+        (n', e) : t ->
+          if n' >= n
+            then e 
+            else pick (n - n') t
+
+-- * Utils
+-------------------------
+
+slices :: Int -> [a] -> [[a]]
+slices size l =
+  case splitAt size l of
+    ([], _) -> []
+    (a, b) -> a : slices size b
+
+
+-- * Main
+-------------------------
+
+main = do
+  -- Pregenerate the transactions:
+  transactionsGroups <- 
+    MWC.runWithCreate $ replicateM (maximum threadsNums) $
+    transactionsGroupGenerator (actionsNum `div` (maximum threadsNums))
+
+  -- Run the benchmark:
+  defaultMain $! flip map threadsNums $ \threadsNum -> 
+    let
+      session = 
+        map concat $! 
+        slices (length transactionsGroups `div` threadsNum) transactionsGroups
+      in
+        bench (shows threadsNum . showString "/" . shows (actionsNum `div` threadsNum) $ "") $
+          scSessionRunner specializedSCInterpreter session
+  where
+    threadsNums = [1, 2, 4, 6, 8, 12, 16, 32, 64, 128]
+    actionsNum = 200000
diff --git a/library/STMContainers/HAMT.hs b/library/STMContainers/HAMT.hs
--- a/library/STMContainers/HAMT.hs
+++ b/library/STMContainers/HAMT.hs
@@ -27,4 +27,4 @@
 
 {-# INLINE null #-}
 null :: HAMT e -> STM Bool
-null v = $notImplemented
+null = Nodes.null
diff --git a/stm-containers.cabal b/stm-containers.cabal
--- a/stm-containers.cabal
+++ b/stm-containers.cabal
@@ -1,7 +1,7 @@
 name:
   stm-containers
 version:
-  0.1.1
+  0.1.2
 synopsis:
   Containers for STM
 description:
@@ -171,15 +171,50 @@
     criterion == 0.8.*,
     mwc-random == 0.13.*,
     mwc-random-monad == 0.7.*,
-    focus > 0.1.0 && < 0.2,
     -- data:
-    stm-containers,
+    text < 1.2,
+    focus > 0.1.0 && < 0.2,
     unordered-containers == 0.2.*,
+    hashable < 1.3,
+    stm-containers,
     -- debugging:
     loch-th == 0.2.*,
     placeholders == 0.1.*,
     -- general:
     free >= 4.5 && < 4.10,
     async == 2.0.*,
+    base >= 4.5 && < 5
+
+
+benchmark concurrent-transactions-bench
+  type: 
+    exitcode-stdio-1.0
+  hs-source-dirs:
+    executables
+  main-is:
+    ConcurrentTransactionsBench.hs
+  ghc-options:
+    -O2 -threaded "-with-rtsopts=-N"
+  default-extensions:
+    Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFunctor, DeriveGeneric, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, ImpredicativeTypes, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
+  default-language:
+    Haskell2010
+  build-depends:
+    criterion == 0.8.*,
+    mwc-random == 0.13.*,
+    mwc-random-monad == 0.7.*,
+    -- data:
+    containers >= 0.5.2 && < 0.6,
+    text < 1.2,
+    focus > 0.1.0 && < 0.2,
+    unordered-containers == 0.2.*,
     hashable < 1.3,
+    stm-containers,
+    -- debugging:
+    loch-th == 0.2.*,
+    placeholders == 0.1.*,
+    -- general:
+    mtl == 2.*,
+    free >= 4.5 && < 4.10,
+    async == 2.0.*,
     base >= 4.5 && < 5
