diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Andrew Martin (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 Andrew Martin 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/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/Maybe/Unpacked/Text/Short.hs b/src/Data/Maybe/Unpacked/Text/Short.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Maybe/Unpacked/Text/Short.hs
@@ -0,0 +1,131 @@
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE UnboxedSums #-}
+{-# LANGUAGE UnboxedTuples #-}
+
+module Data.Maybe.Unpacked.Text.Short
+  ( MaybeShortText(..)
+  , just
+  , nothing
+
+  , maybe
+
+  , isJust
+  , isNothing
+  , fromMaybe
+  , listToMaybe
+  , maybeToList
+  , catMaybes
+  , mapMaybe
+
+  , toBaseMaybe
+  , fromBaseMaybe
+  ) where 
+
+import Prelude hiding (Maybe,maybe)
+
+import GHC.Base (build)
+import Data.ByteString.Short.Internal (ShortByteString(SBS))
+import Data.Text.Short (ShortText,toShortByteString)
+import Data.Text.Short.Unsafe (fromShortByteStringUnsafe)
+import GHC.Exts (ByteArray#)
+
+import GHC.Read (Read(readPrec))
+import Text.Read (parens, Lexeme(Ident), lexP, (+++))
+import Text.ParserCombinators.ReadPrec (prec, step)
+
+import qualified Prelude as P
+
+-- | Either a 'ShortText' or nothing. Do not use the
+-- data constructor directly since it allows you to
+-- circumvent encoding invariants.
+data MaybeShortText = MaybeShortText (# (# #) | ByteArray# #)
+
+unboxShortText :: ShortText -> ByteArray#
+unboxShortText x = case toShortByteString x of SBS y -> y
+
+boxShortText :: ByteArray# -> ShortText
+boxShortText x = fromShortByteStringUnsafe (SBS x)
+
+instance Eq MaybeShortText where
+  ma == mb =
+    maybe (isNothing mb)
+          (\a -> maybe False (\b -> a == b) mb) ma
+    
+instance Ord MaybeShortText where
+  compare ma mb = maybe LT (\a -> maybe GT (compare a) mb) ma  
+
+instance Show MaybeShortText where
+  showsPrec p (MaybeShortText m) = case m of
+    (# (# #) | #) -> showString "nothing"
+    (# | i #) -> showParen (p > 10)
+      $ showString "just "
+      . showsPrec 11 (boxShortText i)
+
+instance Read MaybeShortText where
+  readPrec = parens $ nothingP +++ justP
+    where
+      nothingP = prec 10 $ do
+        Ident "nothing" <- lexP
+        return nothing
+      justP = prec 10 $ do
+        Ident "just" <- lexP
+        a <- step readPrec
+        return (just a)
+
+listToMaybe :: [ShortText] -> MaybeShortText
+listToMaybe [] = nothing
+listToMaybe (x:_) = just x
+
+maybeToList :: MaybeShortText -> [ShortText]
+maybeToList = maybe [] (: [])
+
+catMaybes :: [MaybeShortText] -> [ShortText]
+catMaybes = mapMaybe id
+
+mapMaybe :: (a -> MaybeShortText) -> [a] -> [ShortText]
+mapMaybe _ [] = []
+mapMaybe f (a : as) =
+  let ws = mapMaybe f as
+  in maybe ws (: ws) (f a)
+{-# NOINLINE [1] mapMaybe #-}
+
+{-# RULES
+"mapMaybe"     [~1] forall f xs. mapMaybe f xs
+                    = build (\c n -> foldr (mapMaybeFB c f) n xs)
+"mapMaybeList" [1]  forall f. foldr (mapMaybeFB (:) f) [] = mapMaybe f
+  #-}
+
+{-# NOINLINE [0] mapMaybeFB #-}
+mapMaybeFB :: (ShortText -> r -> r) -> (a -> MaybeShortText) -> a -> r -> r
+mapMaybeFB cons f x next = maybe next (flip cons next) (f x)
+
+isNothing :: MaybeShortText -> Bool
+isNothing = maybe True (const False)
+
+isJust :: MaybeShortText -> Bool
+isJust = maybe False (const True)
+
+nothing :: MaybeShortText
+nothing = MaybeShortText (# (# #) | #)
+
+just :: ShortText -> MaybeShortText
+just x = MaybeShortText (# | unboxShortText x #)
+
+fromMaybe :: ShortText -> MaybeShortText -> ShortText
+fromMaybe a (MaybeShortText m) = case m of
+  (# (# #) | #) -> a
+  (# | i #) -> boxShortText i
+
+maybe :: a -> (ShortText -> a) -> MaybeShortText -> a
+maybe a f (MaybeShortText m) = case m of
+  (# (# #) | #) -> a
+  (# | i #) -> f (boxShortText i)
+
+toBaseMaybe :: MaybeShortText -> P.Maybe ShortText
+toBaseMaybe = maybe P.Nothing P.Just
+
+fromBaseMaybe :: P.Maybe ShortText -> MaybeShortText
+fromBaseMaybe = P.maybe nothing just
+
diff --git a/unpacked-maybe-text.cabal b/unpacked-maybe-text.cabal
new file mode 100644
--- /dev/null
+++ b/unpacked-maybe-text.cabal
@@ -0,0 +1,38 @@
+cabal-version: 2.2
+name: unpacked-maybe-text
+version: 0.1.0.0
+synopsis: optional text that unpacks well
+description:
+  This library provides a specialization of `Maybe ShortText`.
+  When this is an unpacked field of data constructor, this will
+  result in fewer indirections and fewer allocations. This is
+  a micro-optimization, so think hard about whether or not this
+  will improve performance in a particular situation. A different
+  way to accomplish a similar goal is to forbid empty text and then
+  use the empty text value as a placeholder that signifies `Nothing`.
+  This alternative may or may not be an option depending on whether
+  or not the empty string is valid in your problem domain.
+homepage: https://github.com/andrewthad/unpacked-maybe-text
+bug-reports: https://github.com/andrewthad/unpacked-maybe-text/issues
+author: Andrew Martin
+maintainer: andrew.thaddeus@gmail.com
+category: Data
+copyright: 2019 Andrew Martin
+license: BSD-3-Clause
+license-file: LICENSE
+build-type: Simple
+
+source-repository head
+  type: git
+  location: https://github.com/andrewthad/unpacked-maybe-numeric
+
+library
+  exposed-modules:
+    Data.Maybe.Unpacked.Text.Short
+  hs-source-dirs: src
+  build-depends:
+    , base >=4.10.1.0 && <5
+    , text-short >=0.1.3 && <0.2
+    , bytestring >=0.10.8 && <0.11
+  ghc-options: -Wall -O2
+  default-language: Haskell2010
