diff --git a/heidi.cabal b/heidi.cabal
--- a/heidi.cabal
+++ b/heidi.cabal
@@ -1,5 +1,5 @@
 name:           heidi
-version:        0.1.0
+version:        0.2.0
 synopsis:       Tidy data in Haskell
 description:    Tidy data in Haskell, via generics.
 homepage:       https://github.com/ocramz/heidi#readme
@@ -42,7 +42,7 @@
       src
   ghc-options: -Wall
   build-depends:
-                    base > 4.9 && < 4.14
+                    base > 4.9 && < 5
                 , boxes >= 0.1.4
               -- , bytestring >= 0.10.8.1
               , containers >= 0.5.7.1
diff --git a/src/Core/Data/Frame.hs b/src/Core/Data/Frame.hs
--- a/src/Core/Data/Frame.hs
+++ b/src/Core/Data/Frame.hs
@@ -100,7 +100,7 @@
 -- | A 'Frame' is a non-empty list of rows.
 newtype Frame row = Frame {
     -- nFrameRows :: Maybe Int  -- ^ Nothing means unknown
-    tableRows :: NE.NonEmpty row } deriving (Show, Functor, Foldable, Traversable)
+    tableRows :: NE.NonEmpty row } deriving (Show, Functor, Foldable, Traversable, Semigroup)
 
 -- | Take the first row of a 'Frame'
 --
diff --git a/src/Heidi/Data/Row/GenericTrie.hs b/src/Heidi/Data/Row/GenericTrie.hs
--- a/src/Heidi/Data/Row/GenericTrie.hs
+++ b/src/Heidi/Data/Row/GenericTrie.hs
@@ -1,8 +1,8 @@
-{-# language DeriveFunctor #-}
-{-# language DeriveFoldable #-}
-{-# language DeriveGeneric #-}
+
+
+
 {-# language DeriveTraversable #-}
-{-# language TemplateHaskell #-}
+{-# language GeneralizedNewtypeDeriving #-}
 {-# language LambdaCase #-}
 {-# language RankNTypes #-}
 {-# options_ghc -Wno-unused-imports #-}
@@ -116,6 +116,9 @@
 -- * Fast set operations
 -- * Supports missing elements
 newtype Row k v = Row { _unRow :: GT.Trie k v } deriving (Functor, Foldable, Traversable)
+instance (GT.TrieKey k) => Semigroup (Row k v) where
+  Row r1 <> Row r2 = Row $ r1 `GT.union` r2
+instance GT.TrieKey k => Monoid (Row k v) where mempty = Row GT.empty
 -- makeLenses ''Row
 instance (GT.TrieKey k, Show k, Show v) => Show (Row k v) where
   show = show . GT.toList . _unRow
@@ -326,7 +329,7 @@
 eqByLookups :: (Foldable t, GT.TrieKey k, Eq k, Eq a) =>
                t k -> Row k a -> Row k a -> Maybe Bool
 eqByLookups ks r1 r2 = F.foldlM insf True ks where
-  insf b k = (&&) <$> pure b <*> eqByLookup k r1 r2
+  insf b k = pure (((&&)) b) <*> eqByLookup k r1 r2
 
 -- -- | Like 'lookup', but throws a 'KeyError' if the lookup is unsuccessful
 -- lookupThrowM :: (MonadThrow m, Show k, Typeable k, GT.TrieKey k) =>
@@ -382,7 +385,7 @@
 partitionWithKey :: GT.TrieKey k =>
                     (k -> v -> Bool) -- ^ predicate
                  -> Row k v
-                 -> (Row k v, Row k v) 
+                 -> (Row k v, Row k v)
 partitionWithKey qf = foldWithKey insf (empty, empty)
   where
     insf k v (lacc, racc) | qf k v    = (insert k v lacc, racc)
@@ -471,7 +474,7 @@
 
 -- | Inline synonym for 'elemSatisfies'
 (!:) :: (GT.TrieKey k) => k -> (a -> Bool) -> Row k a -> Bool
-k !: f = elemSatisfies f k 
+k !: f = elemSatisfies f k
 
 -- -- | Lookup a value from a Row indexed at the given key (returns in a MonadThrow type)
 -- lookupColM :: (MonadThrow m, Show k, Typeable k, GT.TrieKey k) =>
