diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# Choice: a solution to boolean blindness
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/choice.cabal b/choice.cabal
new file mode 100644
--- /dev/null
+++ b/choice.cabal
@@ -0,0 +1,22 @@
+name:                choice
+version:             0.1.0.0
+synopsis:            A solution to boolean blindness.
+description:         Please see README.md.
+homepage:            https://github.com/mboes/choice#readme
+license:             PublicDomain
+author:              Mathieu Boespflug
+maintainer:          m@tweag.io
+category:            Data
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Data.Choice
+  build-depends:       base >= 4.7 && < 5
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/mboes/choice
diff --git a/src/Data/Choice.hs b/src/Data/Choice.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Choice.hs
@@ -0,0 +1,119 @@
+-- | Represent do\/don't, is\/isn't, with\/without flags with 'Choice'.
+--
+-- <https://existentialtype.wordpress.com/2011/03/15/boolean-blindness/ Boolean blindness>
+-- refers to the problem that boolean literals on their own aren't very
+-- informative. In any given context, what does 'True' mean? What does 'False'
+-- mean? Instead of passing arguments of type 'Bool' to functions, consider
+-- using 'Choice'.
+--
+-- 'Choice' is the type of labeled booleans. Use it as follows:
+--
+-- @
+-- {-\# LANGUAGE OverloadedLabels \#-}
+--
+-- import Data.Choice (Choice, Do, Don't)
+--
+-- -- Blocking read: block until N bytes available.
+-- -- Non-blocking: return as many bytes as are available.
+-- readBytes :: Handle -> Choice "block" -> Int -> IO ByteString
+-- readBytes = ...
+--
+-- action1 = print =<< readBytes h (Don't #block) 1024
+-- @
+--
+-- For GHC < 8.0, where overloaded labels are not available, substitute
+-- @(Label :: Label "block")@ for @#block@.
+--
+-- __A comment on labels:__ why use labels? We could as well ask the user to
+-- define ad hoc constructors. But unlike constructors, labels are guaranteed to
+-- be singletons. That's exactly what we want: a label doesn't carry any runtime
+-- information, it's just a type annotation. Better yet, with labels, there is
+-- no need to ensure that constructor names are unique, nor to pollute the
+-- precious constructor namespace in a large module with many flags.
+
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE MagicHash #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE PatternSynonyms #-}
+{-# LANGUAGE TypeFamilies #-}
+
+{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures #-}
+
+module Data.Choice
+  ( Choice
+  -- * Conversion
+  , fromBool
+  , toBool
+  -- * Choice aliases
+  , pattern Do
+  , pattern Don't
+  , pattern Is
+  , pattern Isn't
+  , pattern With
+  , pattern Without
+  -- * Internal
+  -- $label-export
+  , Label(..)
+  ) where
+
+import GHC.OverloadedLabels (IsLabel(..))
+import GHC.TypeLits (Symbol)
+
+-- $label-export
+--
+-- The 'Label' data type is only exported in full for compatibility with
+-- versions of GHC older than 8.0.
+
+-- | A synonym for 'Data.Proxy.Proxy'.
+data Label (a :: Symbol) = Label deriving (Eq, Ord, Show)
+
+instance x ~ x' => IsLabel x (Label x') where
+  fromLabel _ = Label
+
+-- | A labeled boolean choice.
+data Choice (a :: Symbol)
+  = Off {-# UNPACK #-} !(Label a)
+  | On {-# UNPACK #-} !(Label a)
+  deriving (Eq, Ord)
+
+instance Show (Choice a) where
+  show x = "fromBool " ++ show (toBool x)
+
+-- | Alias for 'True', e.g. @Do #block@.
+pattern Do x = On x
+
+-- | Alias for 'False', e.g. @Don't #block@.
+pattern Don't x = Off x
+
+-- | Alias for 'True', e.g. @Is #ordered@.
+pattern Is x = On x
+
+-- | Alias for 'False', e.g. @Isn't #ordered@.
+pattern Isn't x = Off x
+
+-- | Alias for 'True', e.g. @With #ownDirectory@.
+pattern With x = On x
+
+-- | Alias for 'False', e.g. @Without #ownDirectory@.
+pattern Without x = Off x
+
+instance Enum (Choice a) where
+  toEnum 0 = Off Label
+  toEnum 1 = On Label
+  toEnum _ = error "Prelude.Enum.Choice.toEnum: bad argument"
+
+  fromEnum (Off _) = 0
+  fromEnum (On _) = 1
+
+instance Bounded (Choice a) where
+  minBound = Off Label
+  maxBound = On Label
+
+toBool :: Choice a -> Bool
+toBool (Off _) = False
+toBool (On _) = True
+
+fromBool :: Bool -> Choice a
+fromBool False = Off Label
+fromBool True = On Label
