diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,26 @@
+Copyright (c) Daniel Mendler 2017, Roman Leshchinskiy 2006-2007
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. 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.
+3. Neither the name of the author nor the names of his contributors
+   may be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE 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 AUTHORS 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/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/src/Data/Strict.hs b/src/Data/Strict.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict.hs
@@ -0,0 +1,29 @@
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict
+-- Copyright   :  (c) 2017 Daniel Mendler, 2006-2007 Roman Leshchinskiy
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict versions of some standard Haskell types.
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict (
+    module Data.Strict.Class
+  , module Data.Strict.Tuple
+  , module Data.Strict.Maybe
+  , module Data.Strict.Either
+  , module Data.Strict.List
+  , module Data.Strict.List.NonEmpty
+) where
+
+import Data.Strict.Class
+import Data.Strict.Tuple
+import Data.Strict.Maybe
+import Data.Strict.Either
+import Data.Strict.List
+import Data.Strict.List.NonEmpty
diff --git a/src/Data/Strict/Class.hs b/src/Data/Strict/Class.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/Class.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE FunctionalDependencies #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.Class
+-- Copyright   :  (c) 2017 Daniel Mendler
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- IsStrict isomorphism for conversion.
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.Class (
+  IsStrict(..),
+  liftStrict
+) where
+
+class IsStrict l s | l -> s, s -> l where
+  fromStrict :: s -> l
+  toStrict   :: l -> s
+
+liftStrict :: IsStrict l s => (l -> l) -> (s -> s)
+liftStrict f = toStrict . f . fromStrict
diff --git a/src/Data/Strict/Either.hs b/src/Data/Strict/Either.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/Either.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.Either
+-- Copyright   :  (c) 2017 Daniel Mendler, 2006-2007 Roman Leshchinskiy
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict @Either@.
+--
+-- Same as the standard Haskell @Either@, but @Left _|_ = Right _|_ = _|_@
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.Either (
+    Either(..)
+  , either
+  , isLeft, isRight
+  , lefts, rights
+  , partitionEithers
+) where
+
+import qualified Data.Either as L
+import Prelude hiding (Either(..), either)
+import Data.Bifunctor (Bifunctor(..))
+import GHC.Generics (Generic, Generic1)
+import Data.Data (Data, Typeable)
+import Data.Strict.Class
+
+-- | The strict choice type.
+data Either a b = Left !a | Right !b
+  deriving (Eq, Ord, Read, Show, Functor, Traversable, Foldable, Generic, Generic1, Data, Typeable)
+
+instance IsStrict (L.Either a b) (Either a b) where
+  toStrict   (L.Left x)  = Left x
+  toStrict   (L.Right y) = Right y
+  fromStrict (Left x)    = L.Left x
+  fromStrict (Right y)   = L.Right y
+
+instance Bifunctor Either where
+  bimap f _ (Left a) = Left (f a)
+  bimap _ g (Right a) = Right (g a)
+  first f = either (Left . f) Right
+  second g = either Left (Right . g)
+
+-- | Case analysis: if the value is @'Left' a@, apply the first function to @a@;
+-- if it is @'Right' b@, apply the second function to @b@.
+either :: (a -> c) -> (b -> c) -> Either a b -> c
+either f _ (Left  x) = f x
+either _ g (Right y) = g y
+
+-- | Yields 'True' iff the argument is of the form @Left _@.
+--
+isLeft :: Either a b -> Bool
+isLeft (Left _) = True
+isLeft _        = False
+
+-- | Yields 'True' iff the argument is of the form @Right _@.
+--
+isRight :: Either a b -> Bool
+isRight (Right _) = True
+isRight _         = False
+
+-- | Analogous to 'L.lefts' in "Data.Either".
+lefts :: [Either a b] -> [a]
+lefts x = [a | Left a <- x]
+
+-- | Analogous to 'L.rights' in "Data.Either".
+rights :: [Either a b] -> [b]
+rights x = [a | Right a <- x]
+
+-- | Analogous to 'L.partitionEithers' in "Data.Either".
+partitionEithers :: [Either a b] -> ([a],[b])
+partitionEithers = foldr (either left right) ([], [])
+  where left  a ~(l, r) = (a:l, r)
+        right a ~(l, r) = (l, a:r)
diff --git a/src/Data/Strict/List.hs b/src/Data/Strict/List.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/List.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.List
+-- Copyright   :  (c) 2017 Daniel Mendler
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict @List@.
+--
+-- Same as the standard Haskell List, but strict.
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.List (
+  List(..)
+) where
+
+import GHC.Generics (Generic, Generic1)
+import Data.Data (Data, Typeable)
+import Data.Strict.Trustworthy
+import Data.Strict.Class
+
+infixr 5 :!
+
+-- | The strict list type.
+data List a = Nil | !a :! !(List a)
+  deriving (Eq, Ord, Show, Read, Functor, Foldable, Traversable, Generic, Generic1, Data, Typeable)
+
+instance IsStrict [a] (List a) where
+  toStrict   = foldr (:!) Nil
+  fromStrict = foldr (:) []
+
+instance IsList (List a) where
+  type Item (List a) = a
+  fromList = toStrict
+  toList   = fromStrict
diff --git a/src/Data/Strict/List/NonEmpty.hs b/src/Data/Strict/List/NonEmpty.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/List/NonEmpty.hs
@@ -0,0 +1,50 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.List.NonEmpty
+-- Copyright   :  (c) 2017 Daniel Mendler
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict @NonEmpty@.
+--
+-- Same as the standard Haskell NonEmpty, but strict.
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.List.NonEmpty (
+  NonEmpty(..)
+) where
+
+import qualified Data.List.NonEmpty as L
+import Data.Strict.List
+import GHC.Generics (Generic, Generic1)
+import Data.Data (Data, Typeable)
+import Data.Strict.Trustworthy
+import Data.Strict.Class
+
+infixr 5 :|
+
+-- | The strict list type.
+data NonEmpty a = !a :| !(List a)
+  deriving (Eq, Ord, Show, Read, Functor, Foldable, Traversable, Generic, Generic1, Data, Typeable)
+
+instance IsStrict (L.NonEmpty a) (NonEmpty a) where
+  toStrict   (a L.:| as) = a   :| toStrict as
+  fromStrict (a   :| as) = a L.:| fromStrict as
+
+instance IsList (NonEmpty a) where
+  type Item (NonEmpty a) = a
+  fromList (a : as)  = a :| fromList as
+  fromList []        = error "NonEmpty.fromList: empty list"
+  toList  ~(a :| as) = a : toList as
diff --git a/src/Data/Strict/Maybe.hs b/src/Data/Strict/Maybe.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/Maybe.hs
@@ -0,0 +1,113 @@
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.Maybe
+-- Copyright   :  (c) 2017 Daniel Mendler, 2006-2007 Roman Leshchinskiy
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict @Maybe@.
+--
+-- Same as the standard Haskell @Maybe@, but @Just _|_ = _|_@
+--
+-- Note that strict @Maybe@ is not a monad since
+-- @ return _|_ >>= f = _|_ @
+-- which is not necessarily the same as @f _|_@.
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.Maybe (
+    Maybe(..)
+  , isJust
+  , isNothing
+  , fromMaybe
+  , maybe
+  , listToMaybe
+  , maybeToList
+  , mapMaybe
+  , catMaybes
+) where
+
+import qualified Data.Maybe as L
+import Prelude hiding (Maybe(..), maybe)
+import Data.Semigroup (Semigroup(..))
+import GHC.Generics (Generic, Generic1)
+import Data.Data (Data, Typeable)
+import Data.Strict.Class
+
+-- | The type of strict optional values.
+data Maybe a = Nothing | Just !a
+  deriving (Eq, Ord, Show, Read, Functor, Foldable, Traversable, Generic, Generic1, Data, Typeable)
+
+instance IsStrict (L.Maybe a) (Maybe a) where
+  toStrict   L.Nothing  = Nothing
+  toStrict   (L.Just x) = Just x
+  fromStrict Nothing    = L.Nothing
+  fromStrict (Just x)   = L.Just x
+
+instance Semigroup a => Semigroup (Maybe a) where
+  Nothing <> m       = m
+  m       <> Nothing = m
+  Just x1 <> Just x2 = Just (x1 <> x2)
+
+instance Monoid a => Monoid (Maybe a) where
+  mempty                    = Nothing
+  Nothing `mappend` m       = m
+  m       `mappend` Nothing = m
+  Just x1 `mappend` Just x2 = Just (x1 `mappend` x2)
+
+-- | Yields 'True' iff the argument is of the form @Just _@.
+isJust :: Maybe a -> Bool
+isJust Nothing = False
+isJust _       = True
+
+-- | Yields 'True' iff the argument is 'Nothing'.
+isNothing :: Maybe a -> Bool
+isNothing Nothing = True
+isNothing _       = False
+
+-- | Given a default value and a 'Maybe', yield the default value if the
+-- 'Maybe' argument is 'Nothing' and extract the value out of the 'Just'
+-- otherwise.
+fromMaybe :: a -> Maybe a -> a
+fromMaybe x Nothing  = x
+fromMaybe _ (Just y) = y
+
+-- | Given a default value, a function and a 'Maybe' value, yields the default
+-- value if the 'Maybe' value is 'Nothing' and applies the function to the
+-- value stored in the 'Just' otherwise.
+maybe :: b -> (a -> b) -> Maybe a -> b
+maybe x _ Nothing  = x
+maybe _ f (Just y) = f y
+
+-- | Analogous to 'L.listToMaybe' in "Data.Maybe".
+listToMaybe :: [a] -> Maybe a
+listToMaybe []    = Nothing
+listToMaybe (a:_) = Just a
+
+-- | Analogous to 'L.maybeToList' in "Data.Maybe".
+maybeToList :: Maybe a -> [a]
+maybeToList Nothing  = []
+maybeToList (Just x) = [x]
+
+-- | Analogous to 'L.catMaybes' in "Data.Maybe".
+catMaybes :: [Maybe a] -> [a]
+catMaybes ls = [x | Just x <- ls]
+
+-- | Analogous to 'L.mapMaybe' in "Data.Maybe".
+mapMaybe :: (a -> Maybe b) -> [a] -> [b]
+mapMaybe _ []     = []
+mapMaybe f (x:xs) =
+  case f x of
+    Nothing -> rs
+    Just r  -> r:rs
+  where rs = mapMaybe f xs
diff --git a/src/Data/Strict/Trustworthy.hs b/src/Data/Strict/Trustworthy.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/Trustworthy.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE Trustworthy #-}
+module Data.Strict.Trustworthy (
+  IsList(..)
+) where
+
+import GHC.Exts (IsList(..))
diff --git a/src/Data/Strict/Tuple.hs b/src/Data/Strict/Tuple.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Strict/Tuple.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE DeriveFunctor #-}
+{-# LANGUAGE DeriveFoldable #-}
+{-# LANGUAGE DeriveTraversable #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE DeriveDataTypeable #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module      :  Data.Strict.Tuple
+-- Copyright   :  (c) 2017 Daniel Mendler, 2006-2007 Roman Leshchinskiy
+-- License     :  BSD-style (see the file LICENSE)
+--
+-- Maintainer  :  Daniel Mendler <mail@daniel-mendler.de>
+-- Stability   :  experimental
+-- Portability :  portable
+--
+-- Strict pairs.
+--
+-- Same as regular Haskell pairs, but @(x :!: _|_) = (_|_ :!: y) = _|_@
+--
+-----------------------------------------------------------------------------
+
+module Data.Strict.Tuple (
+    Pair(..)
+  , (:!:)
+  , fst
+  , snd
+  , curry
+  , uncurry
+  , swap
+  , zip
+  , unzip
+) where
+
+import Prelude hiding (fst, snd, curry, uncurry, zip, unzip)
+import Data.Semigroup(Semigroup(..))
+import Data.Bifunctor (Bifunctor(..))
+import Data.Ix (Ix)
+import GHC.Generics (Generic, Generic1)
+import Data.Data (Data, Typeable)
+import Data.Strict.Class
+
+infixl 2 :!:
+
+-- | The type of strict pairs.
+data Pair a b = !a :!: !b
+  deriving (Eq, Ord, Show, Read, Bounded, Ix, Functor, Foldable, Traversable, Generic, Generic1, Data, Typeable)
+
+-- This gives a nicer syntax for the type but only works in GHC for now.
+type (:!:) = Pair
+
+instance IsStrict (a, b) (Pair a b) where
+  toStrict   (a, b)    = a :!: b
+  fromStrict (a :!: b) = (a, b)
+
+instance (Semigroup a, Semigroup b) => Semigroup (Pair a b) where
+  (x1 :!: y1) <> (x2 :!: y2) = (x1 <> x2) :!: (y1 <> y2)
+
+instance (Monoid a, Monoid b) => Monoid (Pair a b) where
+  mempty                            = mempty :!: mempty
+  (x1 :!: y1) `mappend` (x2 :!: y2) = (x1 `mappend` x2) :!: (y1 `mappend` y2)
+instance Bifunctor Pair where
+  bimap f g (a :!: b) = f a :!: g b
+  first f (a :!: b) = f a :!: b
+  second g (a :!: b) = a :!: g b
+
+-- | Extract the first component of a strict pair.
+fst :: Pair a b -> a
+fst (x :!: _) = x
+
+-- | Extract the second component of a strict pair.
+snd :: Pair a b -> b
+snd (_ :!: y) = y
+
+-- | Curry a function on strict pairs.
+curry :: (Pair a b -> c) -> a -> b -> c
+curry f x y = f (x :!: y)
+
+-- | Convert a curried function to a function on strict pairs.
+uncurry :: (a -> b -> c) -> Pair a b -> c
+uncurry f (x :!: y) = f x y
+
+-- | Analagous to 'L.swap' from "Data.Tuple"
+swap :: Pair a b -> Pair b a
+swap (a :!: b) = b :!: a
+
+-- | Zip for strict pairs (defined with zipWith).
+zip :: [a] -> [b] -> [Pair a b]
+zip = zipWith (:!:)
+
+-- | Unzip for stict pairs into a (lazy) pair of lists.
+unzip :: [Pair a b] -> ([a], [b])
+unzip x = (map fst x, map snd x)
diff --git a/strict-base.cabal b/strict-base.cabal
new file mode 100644
--- /dev/null
+++ b/strict-base.cabal
@@ -0,0 +1,37 @@
+Name:           strict-base
+Version:        0.4.0.0
+Synopsis:       Strict versions of base data types.
+Category:       Data, System
+Description:    This package provides strict versions of some standard Haskell data
+                types (Pairs, Maybe, Either, List and NonEmpty).
+License:        BSD3
+License-File:   LICENSE
+Author:         Daniel Mendler <mail@daniel-mendler.de>, Roman Leshchinskiy <rl@cse.unsw.edu.au>
+Maintainer:     Daniel Mendler <mail@daniel-mendler.de>
+Copyright:      (c) 2017 Daniel Mendler, 2006-2007 Roman Leshchinskiy
+Homepage:       https://github.com/minad/strict-base
+Cabal-Version:  >= 1.10
+Build-type:     Simple
+
+source-repository head
+  type: git
+  location: https://github.com/minad/strict-base
+
+library
+  ghc-options:       -Wall
+  default-language:  Haskell2010
+  hs-source-dirs:    src
+  build-depends:     base < 5
+  if impl(ghc < 8.0)
+    build-depends:
+        semigroups >= 0.9 && < 1
+  exposed-modules:
+        Data.Strict.Tuple
+        Data.Strict.Maybe
+        Data.Strict.Either
+        Data.Strict.Class
+        Data.Strict.List
+        Data.Strict.List.NonEmpty
+        Data.Strict
+  other-modules:
+        Data.Strict.Trustworthy
