diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for env-guard
 
+## 0.1.1 -- 2022-07-01
+
+* Add higher-level `withGuard` combinator.
+* Add "OrElse" pattern.
+
 ## 0.1 -- 2022-06-29
 
 * First version. Released on an unsuspecting world.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -5,12 +5,12 @@
 [![Hackage](https://img.shields.io/hackage/v/env-guard)](https://hackage.haskell.org/package/env-guard)
 [![MIT](https://img.shields.io/github/license/tbidne/env-guard?color=blue)](https://opensource.org/licenses/MIT)
 
-[![nix](https://img.shields.io/github/workflow/status/tbidne/env-guard/nix/main?label=nix%209.2.3&&logo=nixos&logoColor=85c5e7&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/nix_ci.yaml)
-[![stack](https://img.shields.io/github/workflow/status/tbidne/env-guard/stack/main?label=stack%2019.4&logoColor=white&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/stack_ci.yaml)
+[![nix](https://img.shields.io/github/workflow/status/tbidne/env-guard/nix/main?label=nix%209.2&&logo=nixos&logoColor=85c5e7&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/nix_ci.yaml)
+[![stack](https://img.shields.io/github/workflow/status/tbidne/env-guard/stack/main?label=stack%2019&logoColor=white&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/stack_ci.yaml)
 [![style](https://img.shields.io/github/workflow/status/tbidne/env-guard/style/main?label=style&logoColor=white&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/style_ci.yaml)
 
-[![8.10.7](https://img.shields.io/github/workflow/status/tbidne/env-guard/8.10.7/main?label=8.10.7&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_8-10.yaml)
-[![9.0.2](https://img.shields.io/github/workflow/status/tbidne/env-guard/9.0.2/main?label=9.0.2&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_9-0.yaml)
-[![9.2.3](https://img.shields.io/github/workflow/status/tbidne/env-guard/9.2.3/main?label=9.2.3&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_9-2.yaml)
+[![8.10](https://img.shields.io/github/workflow/status/tbidne/env-guard/8.10/main?label=8.10&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_8-10.yaml)
+[![9.0](https://img.shields.io/github/workflow/status/tbidne/env-guard/9.0/main?label=9.0&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_9-0.yaml)
+[![9.2](https://img.shields.io/github/workflow/status/tbidne/env-guard/9.2/main?label=9.2&logo=haskell&logoColor=904d8c&labelColor=2f353c)](https://github.com/tbidne/env-guard/actions/workflows/ghc_9-2.yaml)
 
 </div>
diff --git a/env-guard.cabal b/env-guard.cabal
--- a/env-guard.cabal
+++ b/env-guard.cabal
@@ -1,6 +1,6 @@
 cabal-version:      2.4
 name:               env-guard
-version:            0.1
+version:            0.1.1
 license:            MIT
 license-file:       LICENSE
 tested-with:        GHC ==8.10.7 || ==9.0.2 || ==9.2.3
diff --git a/src/System/Environment/Guard.hs b/src/System/Environment/Guard.hs
--- a/src/System/Environment/Guard.hs
+++ b/src/System/Environment/Guard.hs
@@ -1,32 +1,121 @@
-{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE ImportQualifiedPost #-}
 
 -- | Functions for conditionally running 'IO' actions based on an environment
 -- variable.
 --
 -- @since 0.1
 module System.Environment.Guard
-  ( -- * Checking environment variable is set
+  ( -- * High level combinators
+    ExpectEnv (..),
+    withGuard,
+    withGuard_,
+    guardOrElse,
+    guardOrElse',
+
+    -- * Low level functions
+
+    -- ** Checking environment variable is set
     guardSet,
     guardSet_,
 
-    -- * Checking environment variable match
+    -- ** Checking environment variable match
     guardExpected,
     guardExpected_,
 
-    -- * Checking environment variable predicate
+    -- ** Checking environment variable predicate
     guardPredicate,
     guardPredicate_,
   )
 where
 
-import Control.Monad (void)
-import Data.Char (toLower)
-import System.Environment (lookupEnv)
+import System.Environment.Guard.Lifted (ExpectEnv)
+import System.Environment.Guard.Lifted qualified as Lifted
 
 -- $setup
+-- >>> import Control.Monad (void)
 -- >>> import Data.Functor (($>))
 -- >>> import System.Environment (setEnv)
+-- >>> import System.Environment.Guard.Lifted (ExpectEnv (..))
 
+-- | Guards an action behind an environment variable according to
+-- the given expectation.
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> withGuard "FOO" (ExpectEnvEquals "baz") (putStrLn "succeeded")
+-- Nothing
+--
+-- >>> withGuard "FOO" ExpectEnvSet (putStrLn "succeeded")
+-- succeeded
+-- Just ()
+--
+-- @since 0.1.1
+withGuard :: String -> ExpectEnv -> IO a -> IO (Maybe a)
+withGuard = Lifted.withGuard
+
+-- | Variant of 'withGuard' that ignores the return value.
+--
+-- @since 0.1.1
+withGuard_ :: String -> ExpectEnv -> IO a -> IO ()
+withGuard_ = Lifted.withGuard_
+
+-- | @guardOrElse var expect io1 io2@ is equivalent to
+-- @withGuard var expect io1@ except that it runs @io2@ if @io1@ is not run.
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> guardOrElse "FOO" ExpectEnvSet (pure True) (pure "not found")
+-- Right True
+--
+-- >>> guardOrElse "BAR" ExpectEnvSet (pure True) (pure "not found")
+-- Left "not found"
+--
+-- @since 0.1.1
+guardOrElse ::
+  -- | The environment variable.
+  String ->
+  -- | The expectation.
+  ExpectEnv ->
+  -- | The action to run if the expectation succeeds.
+  IO a ->
+  -- | The action to run if the expectation fails.
+  IO e ->
+  -- | The result.
+  IO (Either e a)
+guardOrElse = Lifted.guardOrElse
+
+-- | 'guardOrElse' specialized to the same type so that we always return an
+-- @a@. This can also be used to ignore the return value i.e.
+--
+-- @
+-- guardOrElse' var expect (void io1) io2
+-- @
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> guardOrElse' "FOO" ExpectEnvSet (pure True) (pure False)
+-- True
+--
+-- >>> guardOrElse' "BAR" ExpectEnvSet (pure True) (pure False)
+-- False
+--
+-- >>> guardOrElse' "BAR" ExpectEnvSet (void $ pure True) (putStrLn "not found")
+-- not found
+--
+-- @since 0.1.1
+guardOrElse' ::
+  -- | The environment variable.
+  String ->
+  -- | The expectation.
+  ExpectEnv ->
+  -- | The action to run if the expectation succeeds.
+  IO a ->
+  -- | The action to run if the expectation fails.
+  IO a ->
+  -- | The result.
+  IO a
+guardOrElse' = Lifted.guardOrElse'
+
 -- | @'guardSet' var io@ runs @io@ iff
 --
 -- 1. The environment variable @var@ is set.
@@ -47,13 +136,13 @@
 --
 -- @since 0.1
 guardSet :: String -> IO a -> IO (Maybe a)
-guardSet var = guardPredicate var (const True)
+guardSet = Lifted.guardSet
 
 -- | Variant of 'guardSet' that ignores the return value.
 --
 -- @since 0.1
 guardSet_ :: String -> IO a -> IO ()
-guardSet_ var = void . guardSet var
+guardSet_ = Lifted.guardSet_
 
 -- | @'guardExpected' var expected io@ runs @io@ iff
 --
@@ -61,7 +150,7 @@
 -- 2. @var@'s value equals @expected@. This is __case-insensitive__.
 --
 -- @
--- 'guardExpected' var expected === 'guardPredicate' var (\\a b -> 'fmap' 'toLower' a == 'fmap' 'toLower' b)
+-- 'guardExpected' var expected === 'guardPredicate' var (\\a b -> 'fmap' 'Data.Char.toLower' a == 'fmap' 'Data.Char.toLower' b)
 -- @
 --
 -- ==== __Examples__
@@ -80,19 +169,19 @@
 --
 -- @since 0.1
 guardExpected :: String -> String -> IO a -> IO (Maybe a)
-guardExpected var expected = guardPredicate var (eqCaseInsensitive expected)
+guardExpected = Lifted.guardExpected
 
 -- | Variant of 'guardExpected_' that ignores the return value.
 --
 -- @since 0.1
 guardExpected_ :: String -> String -> IO a -> IO ()
-guardExpected_ var expected = void . guardExpected var expected
+guardExpected_ = Lifted.guardExpected_
 
 -- | Variant of 'guardPredicate' that ignores the return value.
 --
 -- @since 0.1
 guardPredicate_ :: String -> (String -> Bool) -> IO a -> IO ()
-guardPredicate_ var p = void . guardPredicate var p
+guardPredicate_ = Lifted.guardPredicate_
 
 -- | This is the most general way to check an environment variable.
 -- @'guardPredicate' var p io@ runs @io@ iff
@@ -116,11 +205,4 @@
 --
 -- @since 0.1
 guardPredicate :: String -> (String -> Bool) -> IO a -> IO (Maybe a)
-guardPredicate var p io =
-  lookupEnv var
-    >>= \case
-      Just result | p result -> Just <$> io
-      _ -> pure Nothing
-
-eqCaseInsensitive :: String -> String -> Bool
-eqCaseInsensitive a b = fmap toLower a == fmap toLower b
+guardPredicate = Lifted.guardPredicate
diff --git a/src/System/Environment/Guard/Lifted.hs b/src/System/Environment/Guard/Lifted.hs
--- a/src/System/Environment/Guard/Lifted.hs
+++ b/src/System/Environment/Guard/Lifted.hs
@@ -4,15 +4,24 @@
 --
 -- @since 0.1
 module System.Environment.Guard.Lifted
-  ( -- * Checking environment variable is set
+  ( -- * High level combinators
+    ExpectEnv (..),
+    withGuard,
+    withGuard_,
+    guardOrElse,
+    guardOrElse',
+
+    -- * Low level functions
+
+    -- ** Checking environment variable is set
     guardSet,
     guardSet_,
 
-    -- * Checking environment variable match
+    -- ** Checking environment variable match
     guardExpected,
     guardExpected_,
 
-    -- * Checking environment variable predicate
+    -- ** Checking environment variable predicate
     guardPredicate,
     guardPredicate_,
   )
@@ -26,6 +35,127 @@
 -- $setup
 -- >>> import Data.Functor (($>))
 -- >>> import System.Environment (setEnv)
+
+-- | The expectation for an environment variable lookup.
+--
+-- @since 0.1.1
+data ExpectEnv
+  = -- | Expect that the environment variable is set
+    -- (i.e. contents can be anything).
+    --
+    -- @since 0.1.1
+    ExpectEnvSet
+  | -- | Expect that the environment variable is set and the contents equals
+    -- the string. This is __case-insensitive__.
+    --
+    -- @since 0.1.1
+    ExpectEnvEquals String
+  | -- | Expect that the environment variable is set and its contents
+    -- satisfies the predicate.
+    --
+    -- @since 0.1.1
+    ExpectEnvPredicate (String -> Bool)
+
+-- | @since 0.1.1
+instance Show ExpectEnv where
+  showsPrec _ ExpectEnvSet = showString "ExpectEnvSet"
+  showsPrec i (ExpectEnvEquals s) =
+    showParen
+      (i >= 11)
+      (showString "ExpectEnvEquals " . showsPrec 11 s)
+  showsPrec i (ExpectEnvPredicate _) =
+    showParen
+      (i >= 11)
+      (showString "ExpectEnvEquals " . showString "_")
+
+-- | Guards an action behind an environment variable according to
+-- the given expectation.
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> withGuard "FOO" (ExpectEnvEquals "baz") (putStrLn "succeeded")
+-- Nothing
+--
+-- >>> withGuard "FOO" ExpectEnvSet (putStrLn "succeeded")
+-- succeeded
+-- Just ()
+--
+-- @since 0.1.1
+withGuard :: MonadIO m => String -> ExpectEnv -> m a -> m (Maybe a)
+withGuard var expect m =
+  case expect of
+    ExpectEnvSet -> guardSet var m
+    ExpectEnvEquals str -> guardExpected var str m
+    ExpectEnvPredicate p -> guardPredicate var p m
+
+-- | Variant of 'withGuard' that ignores the return value.
+--
+-- @since 0.1.1
+withGuard_ :: MonadIO m => String -> ExpectEnv -> m a -> m ()
+withGuard_ var expect = void . withGuard var expect
+
+-- | @guardOrElse var expect m1 m2@ is equivalent to
+-- @withGuard var expect m1@ except that it runs @m2@ if @m1@ is not run.
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> guardOrElse "FOO" ExpectEnvSet (pure True) (pure "not found")
+-- Right True
+--
+-- >>> guardOrElse "BAR" ExpectEnvSet (pure True) (pure "not found")
+-- Left "not found"
+--
+-- @since 0.1.1
+guardOrElse ::
+  MonadIO m =>
+  -- | The environment variable.
+  String ->
+  -- | The expectation.
+  ExpectEnv ->
+  -- | The action to run if the expectation succeeds.
+  m a ->
+  -- | The action to run if the expectation fails.
+  m e ->
+  -- | The result.
+  m (Either e a)
+guardOrElse var expect m1 m2 =
+  withGuard var expect m1
+    >>= \case
+      Just x -> pure $ Right x
+      Nothing -> Left <$> m2
+
+-- | 'guardOrElse' specialized to the same type so that we always return an
+-- @a@. This can also be used to ignore the return value i.e.
+--
+-- @
+-- guardOrElse' var expect (void m1) m2
+-- @
+--
+-- ==== __Examples__
+-- >>> setEnv "FOO" "bar"
+-- >>> guardOrElse' "FOO" ExpectEnvSet (pure True) (pure False)
+-- True
+--
+-- >>> guardOrElse' "BAR" ExpectEnvSet (pure True) (pure False)
+-- False
+--
+-- >>> guardOrElse' "BAR" ExpectEnvSet (void $ pure True) (putStrLn "not found")
+-- not found
+--
+-- @since 0.1.1
+guardOrElse' ::
+  MonadIO m =>
+  -- | The environment variable.
+  String ->
+  -- | The expectation.
+  ExpectEnv ->
+  -- | The action to run if the expectation succeeds.
+  m a ->
+  -- | The action to run if the expectation fails.
+  m a ->
+  -- | The result.
+  m a
+guardOrElse' var expect m = fmap (either id id) . guardOrElse var expect m
 
 -- | @'guardSet' var io@ runs @io@ iff
 --
