diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,7 @@
+# Changelog for appendmap
+
+## Unreleased changes
+
+## 0.1.1
+
+* Initial release on Hackage.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Alexey Kotlyarov (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 Alexey Kotlyarov 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,46 @@
+# appendmap
+
+[![Build status](https://img.shields.io/travis/koterpillar/appendmap.svg)](https://travis-ci.org/koterpillar/appendmap)
+[![Hackage](https://img.shields.io/hackage/v/appendmap.svg)](https://hackage.haskell.org/package/appendmap)
+[![Stackage LTS](https://www.stackage.org/package/appendmap/badge/lts)](https://www.stackage.org/lts/package/appendmap)
+[![Hackage dependencies](https://img.shields.io/hackage-deps/v/appendmap.svg)](https://packdeps.haskellers.com/feed?needle=appendmap)
+
+A `Data.Map` wrapper with a `Semigroup` and `Monoid` instances that delegate to
+the individual keys.
+
+```haskell
+import qualified Data.Map as Map
+import Data.Map.Append
+
+> one = AppendMap $ Map.fromList [(1, "hello"), (2, "goodbye")]
+> two = AppendMap $ Map.fromList [(1, "world"), (3, "again")]
+
+> unAppendMap (one <> two)
+fromList [(1, "helloworld"), (2, "goodbye"), (3, "again")]
+```
+
+## Motivation
+
+`Data.Map` has a `Semigroup` instance that keeps the values from the first
+argument in case of a key conflict:
+
+```haskell
+import qualified Data.Map as Map
+import Data.Map.Append
+
+> Map.fromList [(1, "hello")] <> Map.fromList [(1, "goodbye")]
+fromList [(1, "hello")]
+```
+
+A different instance has been suggested for a long time
+([1](https://mail.haskell.org/pipermail/libraries/2012-April/017743.html),
+[2](https://ghc.haskell.org/trac/ghc/ticket/1460)), but this is a breaking change and hasn't happened yet (as of 2018-08).
+
+## Development
+
+Source code is formatted with `hindent` _then_ `stylish-haskell`.
+
+### Releasing
+
+* Run `./bumpversion patch|minor|major`. This will create a new version and push
+  it to the repository to be released.
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/appendmap.cabal b/appendmap.cabal
new file mode 100644
--- /dev/null
+++ b/appendmap.cabal
@@ -0,0 +1,54 @@
+-- This file has been generated from package.yaml by hpack version 0.28.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: a323dd29d1810894fafc021571b49e8c3ca23a22516736fe70ec955cec2be6e4
+
+name:           appendmap
+version:        0.1.1
+synopsis:       Map with a Semigroup and Monoid instances delegating to Semigroup of the elements
+description:    Please see the README on GitHub at <https://github.com/koterpillar/appendmap#readme>
+category:       Data Structures
+homepage:       https://github.com/koterpillar/appendmap#readme
+bug-reports:    https://github.com/koterpillar/appendmap/issues
+author:         Alexey Kotlyarov
+maintainer:     a@koterpillar.com
+copyright:      2018 Alexey Kotlyarov
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+cabal-version:  >= 1.10
+extra-source-files:
+    ChangeLog.md
+    README.md
+
+source-repository head
+  type: git
+  location: https://github.com/koterpillar/appendmap
+
+library
+  exposed-modules:
+      Data.Map.Append
+  other-modules:
+      Paths_appendmap
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <5
+    , containers
+  default-language: Haskell2010
+
+test-suite appendmap-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_appendmap
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      appendmap
+    , base >=4.7 && <5
+    , containers
+    , hspec
+  default-language: Haskell2010
diff --git a/src/Data/Map/Append.hs b/src/Data/Map/Append.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Map/Append.hs
@@ -0,0 +1,22 @@
+module Data.Map.Append where
+
+import           Data.Map           (Map)
+import qualified Data.Map           as Map
+
+import           Data.Monoid        hiding ((<>))
+import           Data.Semigroup
+
+import qualified Data.List.NonEmpty as NE
+
+newtype AppendMap k v = AppendMap
+  { unAppendMap :: Map k v
+  }
+
+instance (Ord k, Semigroup v) => Semigroup (AppendMap k v) where
+  AppendMap a <> AppendMap b = AppendMap $ Map.unionWith (<>) a b
+  sconcat = AppendMap . Map.unionsWith (<>) . NE.toList . fmap unAppendMap
+
+instance (Ord k, Semigroup v) => Monoid (AppendMap k v) where
+  mempty = AppendMap Map.empty
+  mappend = (<>)
+  mconcat = AppendMap . Map.unionsWith (<>) . fmap unAppendMap
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,18 @@
+import qualified Data.Map        as Map
+import           Data.Map.Append
+
+import           Data.Semigroup
+
+import           Test.Hspec
+
+main :: IO ()
+main =
+  hspec $
+  describe "AppendMap" $ do
+    it "has empty element" $
+      unAppendMap (mempty :: AppendMap Int String) `shouldBe` Map.empty
+    it "combines elements" $ do
+      let one = AppendMap $ Map.fromList [(1, "hello"), (2, "goodbye")]
+      let two = AppendMap $ Map.fromList [(1, "world"), (3, "again")]
+      unAppendMap (one <> two) `shouldBe`
+        Map.fromList [(1, "helloworld"), (2, "goodbye"), (3, "again")]
