diff --git a/src/Data/Either/Void.hs b/src/Data/Either/Void.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Either/Void.hs
@@ -0,0 +1,50 @@
+{-# language DataKinds #-}
+{-# language ScopedTypeVariables #-}
+{-# language ViewPatterns #-}
+{-# language RankNTypes #-}
+{-# language KindSignatures #-}
+{-# language PatternSynonyms #-}
+{-# language MagicHash #-}
+{-# language GADTSyntax #-}
+{-# language UnliftedNewtypes #-}
+{-# language UnboxedTuples #-}
+
+module Data.Either.Void
+  ( EitherVoid#(..)
+  , pattern LeftVoid#
+  , pattern RightVoid#
+  ) where
+
+import GHC.Exts
+
+-- | Unboxed variant of @Either@. The thing possibly contained by @Just@
+-- has a void runtime representation. Rather than using a sum, like the
+-- more general @Either#@ does, this represents @Left@ with 0 and
+-- @Right@ with 1.
+--
+-- It is recommended that the data constructor not be used directly.
+-- Prefer the two pattern synonyms.
+newtype EitherVoid# :: TYPE ('TupleRep '[]) -> TYPE ('TupleRep '[]) -> TYPE 'WordRep where
+  EitherVoid# :: forall (a :: TYPE ('TupleRep '[])) (b :: TYPE ('TupleRep '[])). Word# -> EitherVoid# a b
+
+{-# COMPLETE RightVoid#, LeftVoid# #-}
+
+pattern RightVoid# :: b -> EitherVoid# a b
+pattern RightVoid# a <- (helperRight -> (# 1##, a #)) where
+  RightVoid# _ = EitherVoid# 1##
+
+helperRight :: forall (a :: TYPE ('TupleRep '[])) (b :: TYPE ('TupleRep '[])).
+  EitherVoid# a b -> (# Word#, b #)
+{-# inline helperRight #-}
+helperRight (EitherVoid# x) =
+  (# x, (unsafeCoerce# :: (# #) -> b) (# #) #)
+
+pattern LeftVoid# :: a -> EitherVoid# a b
+pattern LeftVoid# a <- (helperLeft -> (# 0##, a #)) where
+  LeftVoid# _ = EitherVoid# 0##
+
+helperLeft :: forall (a :: TYPE ('TupleRep '[])) (b :: TYPE ('TupleRep '[])).
+  EitherVoid# a b -> (# Word#, a #)
+{-# inline helperLeft #-}
+helperLeft (EitherVoid# x) =
+  (# x, (unsafeCoerce# :: (# #) -> a) (# #) #)
diff --git a/src/Data/Maybe/Void.hs b/src/Data/Maybe/Void.hs
new file mode 100644
--- /dev/null
+++ b/src/Data/Maybe/Void.hs
@@ -0,0 +1,41 @@
+{-# language DataKinds #-}
+{-# language ScopedTypeVariables #-}
+{-# language ViewPatterns #-}
+{-# language RankNTypes #-}
+{-# language KindSignatures #-}
+{-# language PatternSynonyms #-}
+{-# language MagicHash #-}
+{-# language GADTSyntax #-}
+{-# language UnliftedNewtypes #-}
+{-# language UnboxedTuples #-}
+
+module Data.Maybe.Void
+  ( MaybeVoid#(..)
+  , pattern JustVoid#
+  , pattern NothingVoid#
+  ) where
+
+import GHC.Exts
+
+-- | Unboxed variant of @Maybe@. The thing possibly contained by @Just@
+-- has a void runtime representation. Rather than using a sum, like the
+-- more general @Maybe#@ does, this represents @Nothing@ with 0 and
+-- @Just@ with 1.
+--
+-- It is recommended that the data constructor not be used directly.
+-- Prefer the two pattern synonyms.
+newtype MaybeVoid# :: TYPE ('TupleRep '[]) -> TYPE 'WordRep where
+  MaybeVoid# :: forall (a :: TYPE ('TupleRep '[])). Word# -> MaybeVoid# a
+
+pattern JustVoid# :: a -> MaybeVoid# a
+pattern JustVoid# a <- (helper -> (# 1##, a #)) where
+  JustVoid# _ = MaybeVoid# 1##
+
+helper :: forall (a :: TYPE ('TupleRep '[])).
+  MaybeVoid# a -> (# Word#, a #)
+{-# inline helper #-}
+helper (MaybeVoid# x) =
+  (# x, (unsafeCoerce# :: (# #) -> a) (# #) #)
+
+pattern NothingVoid# :: MaybeVoid# a
+pattern NothingVoid# = MaybeVoid# 0##
diff --git a/unlifted.cabal b/unlifted.cabal
--- a/unlifted.cabal
+++ b/unlifted.cabal
@@ -1,6 +1,6 @@
 cabal-version: 2.2
 name: unlifted
-version: 0.2.0.0
+version: 0.2.1.0
 synopsis: Unlifted and levity-polymorphic types
 description:
   Unlifted and levity-polymorphic variants of several types from
@@ -19,6 +19,8 @@
   exposed-modules:
     Data.Unlifted
     Data.Text.Short.Unlifted
+    Data.Either.Void
+    Data.Maybe.Void
   hs-source-dirs: src
   build-depends:
     , base >=4.16 && <5
