diff --git a/core-program.cabal b/core-program.cabal
--- a/core-program.cabal
+++ b/core-program.cabal
@@ -1,11 +1,11 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.35.0.
+-- This file has been generated from package.yaml by hpack version 0.35.1.
 --
 -- see: https://github.com/sol/hpack
 
 name:           core-program
-version:        0.6.1.1
+version:        0.6.2.1
 synopsis:       Opinionated Haskell Interoperability
 description:    A library to help build command-line programs, both tools and
                 longer-running daemons.
@@ -76,4 +76,5 @@
     , transformers
     , typed-process
     , unix
+    , unliftio-core
   default-language: Haskell2010
diff --git a/lib/Core/Program.hs b/lib/Core/Program.hs
--- a/lib/Core/Program.hs
+++ b/lib/Core/Program.hs
@@ -2,50 +2,50 @@
 
 -- actually, they're there to group implementation too, but hey.
 
--- |
--- Support for building command-line programs, ranging from simple tools to
--- long-running daemons.
---
--- This is intended to be used directly:
---
--- @
--- import "Core.Program"
--- @
---
--- the submodules are mostly there to group documentation.
-module Core.Program
-  ( -- * Executing a program
+{- |
+Support for building command-line programs, ranging from simple tools to
+long-running daemons.
 
-    -- |
-    -- A top-level Program type giving you unified access to logging, concurrency,
-    -- and more.
-    module Core.Program.Context,
-    module Core.Program.Execute,
-    module Core.Program.Threads,
-    module Core.Program.Unlift,
-    module Core.Program.Metadata,
-    module Core.Program.Exceptions,
+This is intended to be used directly:
 
-    -- * Command-line argument parsing
+@
+import "Core.Program"
+@
 
-    -- |
-    -- Including declaring what options your program accepts, generating help, and
-    -- for more complex cases [sub]commands, mandatory arguments, and environment
-    -- variable handling.
-    module Core.Program.Arguments,
+the submodules are mostly there to group documentation.
+-}
+module Core.Program
+    ( -- * Executing a program
 
-    -- * Logging facilities
+      -- |
+      -- A top-level Program type giving you unified access to logging, concurrency,
+      -- and more.
+        module Core.Program.Context
+    , module Core.Program.Execute
+    , module Core.Program.Threads
+    , module Core.Program.Unlift
+    , module Core.Program.Metadata
+    , module Core.Program.Exceptions
 
-    -- |
-    -- Facilities for noting events through your program and doing debugging.
-    module Core.Program.Logging,
+      -- * Command-line argument parsing
 
-    -- |
-    -- There are a few common use cases which require a bit of wrapping to use
-    -- effectively. Watching files for changes and taking action in the event of a
-    -- change is one.
-    module Core.Program.Notify,
-  )
+      -- |
+      -- Including declaring what options your program accepts, generating help, and
+      -- for more complex cases [sub]commands, mandatory arguments, and environment
+      -- variable handling.
+    , module Core.Program.Arguments
+
+      -- * Logging facilities
+
+      -- |
+      -- Facilities for noting events through your program and doing debugging.
+    , module Core.Program.Logging
+      -- |
+      -- There are a few common use cases which require a bit of wrapping to use
+      -- effectively. Watching files for changes and taking action in the event of a
+      -- change is one.
+    , module Core.Program.Notify
+    )
 where
 
 import Core.Program.Arguments
diff --git a/lib/Core/Program/Arguments.hs b/lib/Core/Program/Arguments.hs
--- a/lib/Core/Program/Arguments.hs
+++ b/lib/Core/Program/Arguments.hs
@@ -20,53 +20,53 @@
 Additionally, this module allows you to specify environment variables that,
 if present, will be incorporated into the stored configuration.
 -}
-module Core.Program.Arguments (
-    -- * Setup
-    Config,
-    blankConfig,
-    simpleConfig,
-    complexConfig,
-    baselineOptions,
-    Parameters (..),
-    ParameterValue (..),
+module Core.Program.Arguments
+    ( -- * Setup
+      Config
+    , blankConfig
+    , simpleConfig
+    , complexConfig
+    , baselineOptions
+    , Parameters (..)
+    , ParameterValue (..)
 
-    -- * Options and Arguments
-    LongName (..),
-    ShortName,
-    Description,
-    Options (..),
+      -- * Options and Arguments
+    , LongName (..)
+    , ShortName
+    , Description
+    , Options (..)
 
-    -- * Programs with Commands
-    Commands (..),
-    appendOption,
+      -- * Programs with Commands
+    , Commands (..)
+    , appendOption
 
-    -- * Internals
-    parseCommandLine,
-    extractValidEnvironments,
-    InvalidCommandLine (..),
-    buildUsage,
-    buildVersion,
-    emptyParameters,
-) where
+      -- * Internals
+    , parseCommandLine
+    , extractValidEnvironments
+    , InvalidCommandLine (..)
+    , buildUsage
+    , buildVersion
+    , emptyParameters
+    ) where
 
 import Data.Hashable (Hashable)
 import Data.List qualified as List
 import Data.Maybe (fromMaybe)
 import Data.String (IsString (..))
-import Prettyprinter (
-    Doc,
-    Pretty (..),
-    align,
-    emptyDoc,
-    fillBreak,
-    fillCat,
-    fillSep,
-    hardline,
-    indent,
-    nest,
-    softline,
-    (<+>),
- )
+import Prettyprinter
+    ( Doc
+    , Pretty (..)
+    , align
+    , emptyDoc
+    , fillBreak
+    , fillCat
+    , fillSep
+    , hardline
+    , indent
+    , nest
+    , softline
+    , (<+>)
+    )
 import Prettyprinter.Util (reflow)
 import System.Environment (getProgName)
 
@@ -498,12 +498,12 @@
 
 For options valid in this program, please see --help.
         |]
-             in one ++ two
+            in  one ++ two
         UnknownOption name -> "Sorry, option '" ++ name ++ "' not recognized."
         MissingArgument (LongName name) -> "Mandatory argument '" ++ name ++ "' missing."
         UnexpectedArguments args ->
             let quoted = List.intercalate "', '" args
-             in [quote|
+            in  [quote|
 Unexpected trailing arguments:
 
 |]
@@ -557,7 +557,7 @@
     Complex commands ->
         let globalOptions = extractGlobalOptions commands
             modes = extractValidModes commands
-         in do
+        in  do
                 (possibles, argv') <- splitCommandLine1 argv
                 (params1, _) <- extractor Nothing globalOptions possibles
                 (first, moreArgs) <- splitCommandLine2 argv'
@@ -572,7 +572,7 @@
             valids = extractValidNames options
             shorts = extractShortNames options
             needed = extractRequiredArguments options
-         in do
+        in  do
                 list1 <- parsePossibleOptions mode valids shorts possibles
                 (list2, arguments') <- parseRequiredArguments needed arguments
                 pure (((<>) (intoMap list1) (intoMap list2)), arguments')
@@ -602,12 +602,12 @@
     ('-' : _) -> True
     _ -> False
 
-parsePossibleOptions ::
-    Maybe LongName ->
-    Set LongName ->
-    Map ShortName LongName ->
-    [String] ->
-    Either InvalidCommandLine [(LongName, ParameterValue)]
+parsePossibleOptions
+    :: Maybe LongName
+    -> Set LongName
+    -> Map ShortName LongName
+    -> [String]
+    -> Either InvalidCommandLine [(LongName, ParameterValue)]
 parsePossibleOptions mode valids shorts args = mapM f args
   where
     f arg = case arg of
@@ -626,7 +626,7 @@
             value' = case List.uncons value of
                 Just (_, remainder) -> Value remainder
                 Nothing -> Empty
-         in if containsElement candidate valids
+        in  if containsElement candidate valids
                 then Right (candidate, value')
                 else Left (UnknownOption ("--" ++ name))
 
@@ -636,10 +636,10 @@
             Just name -> Right (name, Empty)
             Nothing -> Left (UnknownOption ['-', c])
 
-parseRequiredArguments ::
-    [LongName] ->
-    [String] ->
-    Either InvalidCommandLine ([(LongName, ParameterValue)], [String])
+parseRequiredArguments
+    :: [LongName]
+    -> [String]
+    -> Either InvalidCommandLine ([(LongName, ParameterValue)], [String])
 parseRequiredArguments needed argv = iter needed argv
   where
     iter :: [LongName] -> [String] -> Either InvalidCommandLine ([(LongName, ParameterValue)], [String])
@@ -650,17 +650,17 @@
     iter (name : _) [] = Left (MissingArgument name)
     iter (name : names) (arg : args) =
         let deeper = iter names args
-         in case deeper of
+        in  case deeper of
                 Left e -> Left e
                 Right (list, remainder) -> Right (((name, Value arg) : list), remainder)
 
-parseIndicatedCommand ::
-    Map LongName [Options] ->
-    String ->
-    Either InvalidCommandLine (LongName, [Options])
+parseIndicatedCommand
+    :: Map LongName [Options]
+    -> String
+    -> Either InvalidCommandLine (LongName, [Options])
 parseIndicatedCommand modes first =
     let candidate = LongName first
-     in case lookupKeyValue candidate modes of
+    in  case lookupKeyValue candidate modes of
             Just options -> Right (candidate, options)
             Nothing -> Left (UnknownCommand first)
 
@@ -722,14 +722,14 @@
 splitCommandLine1 :: [String] -> Either InvalidCommandLine ([String], [String])
 splitCommandLine1 args =
     let (possibles, remainder) = List.span isOption args
-     in if null possibles && null remainder
+    in  if null possibles && null remainder
             then Left NoCommandFound
             else Right (possibles, remainder)
 
 splitCommandLine2 :: [String] -> Either InvalidCommandLine (String, [String])
 splitCommandLine2 argv' =
     let x = List.uncons argv'
-     in case x of
+    in  case x of
             Just (mode, remainingArgs) -> Right (mode, remainingArgs)
             Nothing -> Left NoCommandFound
 
@@ -747,7 +747,7 @@
 
             locals = extractLocalVariables commands (fromMaybe "" mode)
             variables2 = extractVariableNames locals
-         in variables1 <> variables2
+        in  variables1 <> variables2
 
 extractLocalVariables :: [Commands] -> LongName -> [Options]
 extractLocalVariables commands mode =
@@ -777,7 +777,9 @@
     Blank -> emptyDoc
     Simple options ->
         let (o, a, v) = partitionParameters options
-         in "Usage:" <> hardline <> hardline
+        in  "Usage:"
+                <> hardline
+                <> hardline
                 <> indent
                     4
                     ( nest
@@ -802,7 +804,7 @@
             modes = extractValidModes commands
 
             (oG, _, vG) = partitionParameters globalOptions
-         in "Usage:" <> hardline <> hardline <> case mode of
+        in  "Usage:" <> hardline <> hardline <> case mode of
                 Nothing ->
                     indent
                         2
@@ -826,7 +828,7 @@
                     let (oL, aL, vL) = case lookupKeyValue longname modes of
                             Just localOptions -> partitionParameters localOptions
                             Nothing -> error "Illegal State"
-                     in indent
+                    in  indent
                             2
                             ( nest
                                 4
@@ -913,7 +915,7 @@
                 Nothing -> "      --"
             l = pretty longname
             d = fromRope description
-         in case valued of
+        in  case valued of
                 Empty ->
                     fillBreak 16 (s <> l <> " ") <+> align (reflow d) <> hardline <> acc
                 Value label ->
@@ -921,14 +923,14 @@
     g acc (Argument longname description) =
         let l = pretty longname
             d = fromRope description
-         in fillBreak 16 ("  <" <> l <> "> ") <+> align (reflow d) <> hardline <> acc
+        in  fillBreak 16 ("  <" <> l <> "> ") <+> align (reflow d) <> hardline <> acc
     g acc (Remaining description) =
         let d = fromRope description
-         in fillBreak 16 ("  " <> "... ") <+> align (reflow d) <> hardline <> acc
+        in  fillBreak 16 ("  " <> "... ") <+> align (reflow d) <> hardline <> acc
     g acc (Variable longname description) =
         let l = pretty longname
             d = fromRope description
-         in fillBreak 16 ("  " <> l <> " ") <+> align (reflow d) <> hardline <> acc
+        in  fillBreak 16 ("  " <> l <> " ") <+> align (reflow d) <> hardline <> acc
 
     formatCommands :: [Commands] -> Doc ann
     formatCommands commands = hardline <> List.foldl' h emptyDoc commands
@@ -937,12 +939,12 @@
     h acc (Command longname description _) =
         let l = pretty longname
             d = fromRope description
-         in acc <> fillBreak 16 ("  " <> l <> " ") <+> align (reflow d) <> hardline
+        in  acc <> fillBreak 16 ("  " <> l <> " ") <+> align (reflow d) <> hardline
     h acc _ = acc
 
 buildVersion :: Version -> Doc ann
 buildVersion version =
     pretty (projectNameFrom version)
         <+> "v"
-        <> pretty (versionNumberFrom version)
-        <> hardline
+            <> pretty (versionNumberFrom version)
+            <> hardline
diff --git a/lib/Core/Program/Context.hs b/lib/Core/Program/Context.hs
--- a/lib/Core/Program/Context.hs
+++ b/lib/Core/Program/Context.hs
@@ -13,29 +13,29 @@
 {-# OPTIONS_HADDOCK hide #-}
 
 -- This is an Internal module, hidden from Haddock
-module Core.Program.Context (
-    Datum (..),
-    emptyDatum,
-    Trace (..),
-    unTrace,
-    Span (..),
-    unSpan,
-    Context (..),
-    handleCommandLine,
-    handleVerbosityLevel,
-    handleTelemetryChoice,
-    Exporter (..),
-    Forwarder (..),
-    None (..),
-    isNone,
-    configure,
-    Verbosity (..),
-    Program (..),
-    unProgram,
-    getContext,
-    fmapContext,
-    subProgram,
-) where
+module Core.Program.Context
+    ( Datum (..)
+    , emptyDatum
+    , Trace (..)
+    , unTrace
+    , Span (..)
+    , unSpan
+    , Context (..)
+    , handleCommandLine
+    , handleVerbosityLevel
+    , handleTelemetryChoice
+    , Exporter (..)
+    , Forwarder (..)
+    , None (..)
+    , isNone
+    , configure
+    , Verbosity (..)
+    , Program (..)
+    , unProgram
+    , getContext
+    , fmapContext
+    , subProgram
+    ) where
 
 import Control.Concurrent (ThreadId)
 import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, putMVar, readMVar)
@@ -43,6 +43,7 @@
 import Control.Concurrent.STM.TVar (TVar, newTVarIO)
 import Control.Exception.Safe qualified as Safe (throw)
 import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow (throwM))
+import Control.Monad.IO.Unlift (MonadUnliftIO (withRunInIO))
 import Control.Monad.Reader.Class (MonadReader (..))
 import Control.Monad.Trans.Reader (ReaderT (..))
 import Core.Data.Clock
@@ -198,7 +199,7 @@
     state <- readMVar (applicationDataFrom context)
     let state' = f state
     u <- newMVar state'
-    return (context{applicationDataFrom = u})
+    return (context {applicationDataFrom = u})
 
 {- |
 A 'Program' with no user-supplied state to be threaded throughout the
@@ -318,6 +319,18 @@
 subProgram context (Program r) = do
     runReaderT r context
 
+--
+-- This isn't needed by our packages, but it's a useful instance. This is a
+-- copy of what is in Core.Program.Unlift.withContext. I would have put this
+-- there, but it leaves an orphan.
+--
+instance MonadUnliftIO (Program τ) where
+    {-# INLINE withRunInIO #-}
+    withRunInIO action = do
+        context <- getContext
+        liftIO $ do
+            action (subProgram context)
+
 {-
 This is complicated. The **safe-exceptions** library exports a `throwM` which
 is not the `throwM` class method from MonadThrow. See
@@ -487,7 +500,7 @@
 queryVerbosityLevel params =
     let debug = lookupKeyValue "debug" (parameterValuesFrom params)
         verbose = lookupKeyValue "verbose" (parameterValuesFrom params)
-     in case debug of
+    in  case debug of
             Just value -> case value of
                 Empty -> Right Debug
                 Value "internal" -> Right Internal
diff --git a/lib/Core/Program/Exceptions.hs b/lib/Core/Program/Exceptions.hs
--- a/lib/Core/Program/Exceptions.hs
+++ b/lib/Core/Program/Exceptions.hs
@@ -72,30 +72,30 @@
 back to the surface (in our case the 'Program' @τ@ monad) and you can 'throw'
 from there.
 -}
-module Core.Program.Exceptions (
-    catch,
-    try,
-    throw,
-    bracket,
-    finally,
-    onException,
-    Boom (..),
-) where
+module Core.Program.Exceptions
+    ( catch
+    , try
+    , throw
+    , bracket
+    , finally
+    , onException
+    , Boom (..)
+    ) where
 
-import Control.Exception qualified as Base (
-    Exception,
- )
-import Control.Exception.Safe qualified as Safe (
-    bracket,
-    catch,
-    finally,
-    onException,
-    throw,
-    try,
- )
-import Core.Program.Context (
-    Program,
- )
+import Control.Exception qualified as Base
+    ( Exception
+    )
+import Control.Exception.Safe qualified as Safe
+    ( bracket
+    , catch
+    , finally
+    , onException
+    , throw
+    , try
+    )
+import Core.Program.Context
+    ( Program
+    )
 
 {- |
 Catch an exception.
@@ -188,7 +188,7 @@
 
 @
 22:54:39Z (00.002) SomeoneWrongOnInternet \"Ashley thinks there are more than three Star Wars movies\"
-$
+\$
 @
 
 but if you're in a hurry and don't want to define a local exception type to
diff --git a/lib/Core/Program/Execute.hs b/lib/Core/Program/Execute.hs
--- a/lib/Core/Program/Execute.hs
+++ b/lib/Core/Program/Execute.hs
@@ -49,98 +49,100 @@
 arguments you can initialize your program using 'configure' and then run
 with 'executeWith'.
 -}
-module Core.Program.Execute (
-    Program (),
+module Core.Program.Execute
+    ( Program ()
 
-    -- * Running programs
-    configure,
-    execute,
-    executeWith,
+      -- * Running programs
+    , configure
+    , execute
+    , executeWith
 
-    -- * Exiting a program
-    terminate,
+      -- * Exiting a program
+    , terminate
 
-    -- * Accessing program context
-    getCommandLine,
-    queryCommandName,
-    queryOptionFlag,
-    queryOptionValue,
-    queryOptionValue',
-    queryArgument,
-    queryRemaining,
-    queryEnvironmentValue,
-    getProgramName,
-    setProgramName,
-    getVerbosityLevel,
-    setVerbosityLevel,
-    getConsoleWidth,
-    getApplicationState,
-    setApplicationState,
+      -- * Accessing program context
+    , getCommandLine
+    , queryCommandName
+    , queryOptionFlag
+    , queryOptionValue
+    , queryOptionValue'
+    , queryArgument
+    , queryRemaining
+    , queryEnvironmentValue
+    , queryEnvironmentValue'
+    , getProgramName
+    , setProgramName
+    , getVerbosityLevel
+    , setVerbosityLevel
+    , getConsoleWidth
+    , getApplicationState
+    , setApplicationState
 
-    -- * Useful actions
-    outputEntire,
-    inputEntire,
-    execProcess,
-    sleepThread,
-    resetTimer,
-    trap_,
+      -- * Useful actions
+    , outputEntire
+    , inputEntire
+    , execProcess
+    , sleepThread
+    , resetTimer
+    , trap_
 
-    -- * Exception handling
-    catch,
-    throw,
-    try,
+      -- * Exception handling
+    , catch
+    , throw
+    , try
 
-    -- * Internals
-    Context,
-    None (..),
-    isNone,
-    unProgram,
-    invalid,
-    Boom (..),
-    loopForever,
-    lookupOptionFlag,
-    lookupOptionValue,
-    lookupArgument,
-    lookupEnvironmentValue,
-) where
+      -- * Internals
+    , Context
+    , None (..)
+    , isNone
+    , unProgram
+    , invalid
+    , Boom (..)
+    , loopForever
+    , lookupOptionFlag
+    , lookupOptionValue
+    , lookupArgument
+    , lookupEnvironmentValue
+    )
+where
 
-import Control.Concurrent (
-    forkFinally,
-    forkIO,
-    killThread,
-    myThreadId,
-    threadDelay,
- )
-import Control.Concurrent.MVar (
-    MVar,
-    modifyMVar_,
-    newEmptyMVar,
-    putMVar,
-    readMVar,
-    tryPutMVar,
- )
+import Control.Concurrent
+    ( forkFinally
+    , forkIO
+    , killThread
+    , myThreadId
+    , threadDelay
+    )
+import Control.Concurrent.MVar
+    ( MVar
+    , modifyMVar_
+    , newEmptyMVar
+    , putMVar
+    , readMVar
+    , tryPutMVar
+    )
 import Control.Concurrent.STM (atomically)
-import Control.Concurrent.STM.TQueue (
-    TQueue,
-    readTQueue,
-    tryReadTQueue,
-    unGetTQueue,
-    writeTQueue,
- )
-import Control.Concurrent.STM.TVar (
-    readTVarIO,
- )
+import Control.Concurrent.STM.TQueue
+    ( TQueue
+    , readTQueue
+    , tryReadTQueue
+    , unGetTQueue
+    , writeTQueue
+    )
+import Control.Concurrent.STM.TVar
+    ( readTVarIO
+    )
 import Control.Exception qualified as Base (throwIO)
-import Control.Exception.Safe qualified as Safe (
-    catch,
-    throw,
- )
-import Control.Monad (
-    forM_,
-    forever,
-    void,
-    when,
- )
+import Control.Exception.Safe qualified as Safe
+    ( catch
+    , throw
+    )
+import Control.Monad
+    ( forM_
+    , forever
+    , void
+    , when
+    )
 import Control.Monad.Reader.Class (MonadReader (ask))
 import Core.Data.Clock
 import Core.Data.Structures
@@ -150,15 +152,15 @@
 import Core.Program.Exceptions
 import Core.Program.Logging
 import Core.Program.Signal
-import Core.System.Base (
-    Exception,
-    Handle,
-    SomeException,
-    displayException,
-    hFlush,
-    liftIO,
-    stdout,
- )
+import Core.System.Base
+    ( Exception
+    , Handle
+    , SomeException
+    , displayException
+    , hFlush
+    , liftIO
+    , stdout
+    )
 import Core.Text.Bytes
 import Core.Text.Rope
 import Data.ByteString qualified as B (hPut)
@@ -166,9 +168,9 @@
 import Data.List qualified as List (intersperse)
 import GHC.Conc (getNumProcessors, numCapabilities, setNumCapabilities)
 import GHC.IO.Encoding (setLocaleEncoding, utf8)
-import System.Directory (
-    findExecutable,
- )
+import System.Directory
+    ( findExecutable
+    )
 import System.Exit (ExitCode (..))
 import System.Process.Typed (nullStream, proc, readProcess, setStdin)
 import Prelude hiding (log)
@@ -203,7 +205,7 @@
         (void action)
         ( \(e :: SomeException) ->
             let text = intoRope (displayException e)
-             in do
+            in  do
                     warn "Trapped uncaught exception"
                     debug "e" text
         )
@@ -637,7 +639,7 @@
         task = proc cmd' args'
         task1 = setStdin nullStream task
         command = mconcat (List.intersperse (singletonRope ' ') (cmd : args))
-     in do
+    in  do
             debug "command" command
 
             probe <- liftIO $ do
@@ -698,7 +700,7 @@
 sleepThread :: Rational -> Program τ ()
 sleepThread seconds =
     let us = floor (toRational (seconds * 1e6))
-     in liftIO $ threadDelay us
+    in  liftIO $ threadDelay us
 
 {- |
 Retrieve the values of parameters parsed from options and arguments supplied
@@ -824,13 +826,17 @@
 
 data QueryParameterError
     = OptionValueMissing LongName
-    | UnableParseValue LongName
+    | UnableParseOption LongName
+    | EnvironmentVariableMissing LongName
+    | UnableParseVariable LongName
     deriving (Show)
 
 instance Exception QueryParameterError where
     displayException e = case e of
         OptionValueMissing (LongName name) -> "Option --" ++ name ++ " specified but without a value."
-        UnableParseValue (LongName name) -> "Unable to parse the value supplied to --" ++ name ++ "."
+        UnableParseOption (LongName name) -> "Unable to parse the value supplied to --" ++ name ++ "."
+        EnvironmentVariableMissing (LongName name) -> "Variable " ++ name ++ " requested but is unset."
+        UnableParseVariable (LongName name) -> "Unable to parse the value present in " ++ name ++ "."
 
 {- |
 Look to see if the user supplied a valued option and if so, what its value
@@ -869,7 +875,7 @@
         Just parameter -> case parameter of
             Empty -> throw (OptionValueMissing name)
             Value value -> case parseExternal (packRope value) of
-                Nothing -> throw (UnableParseValue name)
+                Nothing -> throw (UnableParseOption name)
                 Just actual -> pure (Just actual)
 
 {- |
@@ -914,6 +920,32 @@
         Just param -> case param of
             Empty -> pure Nothing
             Value str -> pure (Just (intoRope str))
+
+{- |
+Look to see if the user supplied the named environment variable and if so,
+return what its value was.
+
+Like 'queryOptionValue'' above, this function attempts to parse the supplied
+value as 'Just' the inferred type. This makes the assumption that the
+requested environment variable is populated. If it is not set in the
+environment, or is set to the empty string, then this function will return
+'Nothing'.
+
+If the attempt to parse the supplied value fails an exception will be thrown.
+
+@since 0.6.2
+-}
+queryEnvironmentValue' :: Externalize ξ => LongName -> Program τ (Maybe ξ)
+queryEnvironmentValue' name = do
+    context <- ask
+    let params = commandLineFrom context
+    case lookupKeyValue name (environmentValuesFrom params) of
+        Nothing -> error "Attempted lookup of unconfigured environment variable"
+        Just param -> case param of
+            Empty -> pure Nothing
+            Value value -> case parseExternal (packRope value) of
+                Nothing -> throw (UnableParseVariable name)
+                Just actual -> pure (Just actual)
 
 lookupEnvironmentValue :: LongName -> Parameters -> Maybe String
 lookupEnvironmentValue name params =
diff --git a/lib/Core/Program/Logging.hs b/lib/Core/Program/Logging.hs
--- a/lib/Core/Program/Logging.hs
+++ b/lib/Core/Program/Logging.hs
@@ -121,32 +121,32 @@
 \$
 @
 -}
-module Core.Program.Logging (
-    putMessage,
-    formatLogMessage,
-    Severity (..),
-    Verbosity (..),
+module Core.Program.Logging
+    ( putMessage
+    , formatLogMessage
+    , Severity (..)
+    , Verbosity (..)
 
-    -- * Normal output
-    write,
-    writeS,
-    writeR,
+      -- * Normal output
+    , write
+    , writeS
+    , writeR
 
-    -- * Informational
-    info,
-    warn,
-    critical,
+      -- * Informational
+    , info
+    , warn
+    , critical
 
-    -- * Debugging
-    debug,
-    debugS,
-    debugR,
+      -- * Debugging
+    , debug
+    , debugS
+    , debugR
     -- internal
-    internal,
-    isEvent,
-    isDebug,
-    isInternal,
-) where
+    , internal
+    , isEvent
+    , isDebug
+    , isInternal
+    ) where
 
 import Control.Concurrent.MVar (readMVar)
 import Control.Concurrent.STM (atomically)
@@ -221,7 +221,7 @@
             SeverityInternal -> intoEscapes dullBlue
 
         !reset = intoEscapes resetColour
-     in case coloured of
+    in  case coloured of
             True ->
                 mconcat
                     [ intoEscapes dullWhite
diff --git a/lib/Core/Program/Metadata.hs b/lib/Core/Program/Metadata.hs
--- a/lib/Core/Program/Metadata.hs
+++ b/lib/Core/Program/Metadata.hs
@@ -8,18 +8,18 @@
 Digging metadata out of the description of your project, and other useful
 helpers.
 -}
-module Core.Program.Metadata (
-    Version,
-    versionNumberFrom,
-    projectNameFrom,
-    projectSynopsisFrom,
+module Core.Program.Metadata
+    ( Version
+    , versionNumberFrom
+    , projectNameFrom
+    , projectSynopsisFrom
 
-    -- * Splice
-    fromPackage,
+      -- * Splice
+    , fromPackage
 
-    -- * Source code
-    __LOCATION__,
-) where
+      -- * Source code
+    , __LOCATION__
+    ) where
 
 import Core.Data
 import Core.System.Base (IOMode (..), withFile)
@@ -66,7 +66,7 @@
 emptyVersion = Version "" "" "0"
 
 instance IsString Version where
-    fromString x = emptyVersion{versionNumberFrom = x}
+    fromString x = emptyVersion {versionNumberFrom = x}
 
 {- |
 This is a splice which includes key built-time metadata, including the number
@@ -154,7 +154,7 @@
 parseCabalFile :: Bytes -> Map Rope Rope
 parseCabalFile contents =
     let breakup = intoMap . fmap (\(a, b) -> (a, trimValue b)) . fmap (breakRope (== ':')) . breakLines . fromBytes
-     in breakup contents
+    in  breakup contents
 
 -- knock off the colon and whitespace in ":      hello"
 trimValue :: Rope -> Rope
diff --git a/lib/Core/Program/Notify.hs b/lib/Core/Program/Notify.hs
--- a/lib/Core/Program/Notify.hs
+++ b/lib/Core/Program/Notify.hs
@@ -5,10 +5,9 @@
 Helpers for watching files for changes and taking action in the event of a
 change.
 -}
-module Core.Program.Notify (
-  -- * Notify
-  waitForChange,
-) where
+module Core.Program.Notify
+    ( waitForChange
+    ) where
 
 import Control.Concurrent.MVar (newEmptyMVar, putMVar, readMVar)
 import Control.Monad.IO.Class (liftIO)
@@ -40,50 +39,50 @@
 --
 waitForChange :: [FilePath] -> Program τ ()
 waitForChange files =
-  let f :: FilePath -> Set FilePath -> Set FilePath
-      f path acc = insertElement path acc
+    let f :: FilePath -> Set FilePath -> Set FilePath
+        f path acc = insertElement path acc
 
-      g :: FilePath -> Set FilePath -> Set FilePath
-      g path acc = insertElement (dropFileName path) acc
-   in do
-        info "Watching for changes"
+        g :: FilePath -> Set FilePath -> Set FilePath
+        g path acc = insertElement (dropFileName path) acc
+    in  do
+            info "Watching for changes"
 
-        canonical <- mapM (liftIO . canonicalizePath) files
-        let paths = foldr f emptySet canonical
-        let dirs = foldr g emptySet files
+            canonical <- mapM (liftIO . canonicalizePath) files
+            let paths = foldr f emptySet canonical
+            let dirs = foldr g emptySet files
 
-        withContext $ \runProgram -> do
-          block <- newEmptyMVar
-          withManager $ \manager -> do
-            -- setup watches
-            stoppers <-
-              foldrM
-                ( \dir acc -> do
-                    runProgram (debugS "watching" dir)
-                    stopper <-
-                      watchDir
-                        manager
-                        dir
-                        ( \trigger -> case trigger of
-                            Modified file _ _ -> do
-                              if containsElement file paths
-                                then True
-                                else False
-                            _ -> False
-                        )
-                        ( \trigger -> do
-                            runProgram (debugS "trigger" (eventPath trigger))
-                            putMVar block False
-                        )
-                    return (stopper : acc)
-                )
-                []
-                dirs
+            withContext $ \runProgram -> do
+                block <- newEmptyMVar
+                withManager $ \manager -> do
+                    -- setup watches
+                    stoppers <-
+                        foldrM
+                            ( \dir acc -> do
+                                runProgram (debugS "watching" dir)
+                                stopper <-
+                                    watchDir
+                                        manager
+                                        dir
+                                        ( \trigger -> case trigger of
+                                            Modified file _ _ -> do
+                                                if containsElement file paths
+                                                    then True
+                                                    else False
+                                            _ -> False
+                                        )
+                                        ( \trigger -> do
+                                            runProgram (debugS "trigger" (eventPath trigger))
+                                            putMVar block False
+                                        )
+                                return (stopper : acc)
+                            )
+                            []
+                            dirs
 
-            -- wait
-            _ <- readMVar block
+                    -- wait
+                    _ <- readMVar block
 
-            sequence_ stoppers
-            return ()
+                    sequence_ stoppers
+                    return ()
 
-        sleepThread 0.1
+            sleepThread 0.1
diff --git a/lib/Core/Program/Signal.hs b/lib/Core/Program/Signal.hs
--- a/lib/Core/Program/Signal.hs
+++ b/lib/Core/Program/Signal.hs
@@ -1,21 +1,21 @@
 {-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
 
-module Core.Program.Signal (
-    setupSignalHandlers,
-) where
+module Core.Program.Signal
+    ( setupSignalHandlers
+    ) where
 
 import Control.Concurrent.MVar (MVar, modifyMVar_, putMVar)
 import Core.Program.Context
 import Foreign.C.Types (CInt)
 import System.Exit (ExitCode (..))
 import System.IO (hFlush, hPutStrLn, stdout)
-import System.Posix.Signals (
-    Handler (Catch),
-    installHandler,
-    sigINT,
-    sigTERM,
-    sigUSR1,
- )
+import System.Posix.Signals
+    ( Handler (Catch)
+    , installHandler
+    , sigINT
+    , sigTERM
+    , sigUSR1
+    )
 
 {- |
 Make a non-zero exit code which is 0b1000000 + the number of the signal.
diff --git a/lib/Core/Program/Threads.hs b/lib/Core/Program/Threads.hs
--- a/lib/Core/Program/Threads.hs
+++ b/lib/Core/Program/Threads.hs
@@ -23,39 +23,39 @@
 Note that when you fire off a new thread the top-level application state is
 /shared/; it's the same @τ@ inherited from the parent 'Program'.
 -}
-module Core.Program.Threads (
-    -- * Concurrency
-    createScope,
-    forkThread,
-    forkThread_,
-    linkThread,
-    waitThread,
-    waitThread_,
-    waitThread',
-    waitThreads',
-    cancelThread,
+module Core.Program.Threads
+    ( -- * Concurrency
+      createScope
+    , forkThread
+    , forkThread_
+    , linkThread
+    , waitThread
+    , waitThread_
+    , waitThread'
+    , waitThreads'
+    , cancelThread
 
-    -- * Helper functions
-    concurrentThreads,
-    concurrentThreads_,
-    raceThreads,
-    raceThreads_,
+      -- * Helper functions
+    , concurrentThreads
+    , concurrentThreads_
+    , raceThreads
+    , raceThreads_
 
-    -- * Internals
-    Thread,
-    unThread,
-) where
+      -- * Internals
+    , Thread
+    , unThread
+    ) where
 
 import Control.Concurrent (ThreadId, forkIO, killThread)
 import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, putMVar, readMVar)
 import Control.Concurrent.STM (atomically)
 import Control.Concurrent.STM.TVar (modifyTVar', newTVarIO, readTVarIO)
 import Control.Exception.Safe qualified as Safe (catch, finally, onException, throw)
-import Control.Monad (
-    forM,
-    forM_,
-    void,
- )
+import Control.Monad
+    ( forM
+    , forM_
+    , void
+    )
 import Control.Monad.Reader.Class (MonadReader (ask))
 import Core.Data.Structures
 import Core.Program.Context
diff --git a/lib/Core/Program/Unlift.hs b/lib/Core/Program/Unlift.hs
--- a/lib/Core/Program/Unlift.hs
+++ b/lib/Core/Program/Unlift.hs
@@ -93,14 +93,14 @@
 you're having trouble with the types try putting @return ()@ at the end of
 your subprogram.
 -}
-module Core.Program.Unlift (
-    -- * Unlifting
-    withContext,
+module Core.Program.Unlift
+    ( -- * Unlifting
+      withContext
 
-    -- * Internals
-    getContext,
-    subProgram,
-) where
+      -- * Internals
+    , getContext
+    , subProgram
+    ) where
 
 import Core.Program.Context
 import Core.Program.Execute
@@ -145,15 +145,17 @@
             -- now in Program monad
         ...
 @
+
+Note there is a 'MonadUnliftIO' instance which may be useful to you as well.
 -}
 
 -- I think I just discovered the same pattern as **unliftio**? Certainly
 -- the signature is similar. I'm not sure if there is any benefit to
 -- restating this as a `withRunInIO` action; we're deliberately trying to
 -- constrain the types.
-withContext ::
-    ((forall β. Program τ β -> IO β) -> IO α) ->
-    Program τ α
+withContext
+    :: ((forall β. Program τ β -> IO β) -> IO α)
+    -> Program τ α
 withContext action = do
     context <- getContext
     let runThing = subProgram context
diff --git a/lib/Core/System.hs b/lib/Core/System.hs
--- a/lib/Core/System.hs
+++ b/lib/Core/System.hs
@@ -17,32 +17,32 @@
 
 as there's no particular benefit to cherry-picking the various sub-modules.
 -}
-module Core.System (
-    -- * Base libraries
+module Core.System
+    ( -- * Base libraries
 
-    -- |
-    -- Re-exports from foundational libraries supplied by the compiler runtime,
-    -- or from re-implementations of those areas.
-    module Core.System.Base,
+      -- |
+      -- Re-exports from foundational libraries supplied by the compiler runtime,
+      -- or from re-implementations of those areas.
+        module Core.System.Base
 
-    -- * External dependencies
+      -- * External dependencies
 
-    -- |
-    -- Dependencies from libraries outside the traditional ecosystem of Haskell.
-    -- These are typically special cases or custom re-implementations of things
-    -- which are maintained either by ourselves or people we are in regular
-    -- contact with.
-    module Core.System.External,
+      -- |
+      -- Dependencies from libraries outside the traditional ecosystem of Haskell.
+      -- These are typically special cases or custom re-implementations of things
+      -- which are maintained either by ourselves or people we are in regular
+      -- contact with.
+    , module Core.System.External
 
-    -- * Pretty Printing
+      -- * Pretty Printing
 
-    -- |
-    -- When using the Render typeclass from "Core.Text.Utilities" you are
-    -- presented with the @Doc a@ type for accumulating a \"document\" to be
-    -- pretty printed. There are a large family of combinators used when doing
-    -- this. For convenience they are exposed here.
-    module Core.System.Pretty,
-) where
+      -- |
+      -- When using the Render typeclass from "Core.Text.Utilities" you are
+      -- presented with the @Doc a@ type for accumulating a \"document\" to be
+      -- pretty printed. There are a large family of combinators used when doing
+      -- this. For convenience they are exposed here.
+    , module Core.System.Pretty
+    ) where
 
 import Core.System.Base
 import Core.System.External
diff --git a/lib/Core/System/Base.hs b/lib/Core/System/Base.hs
--- a/lib/Core/System/Base.hs
+++ b/lib/Core/System/Base.hs
@@ -5,37 +5,36 @@
 {- |
 Re-exports of Haskell base and GHC system libraries.
 -}
-module Core.System.Base (
-    -- * Input/Output
-
-    -- ** from Control.Monad.IO.Class
+module Core.System.Base
+    ( -- * Input/Output
 
-    -- | Re-exported from "Control.Monad.IO.Class" in __base__:
-    liftIO,
-    MonadIO,
+      -- ** from Control.Monad.IO.Class
 
-    -- ** from System.IO
+      -- | Re-exported from "Control.Monad.IO.Class" in __base__:
+      liftIO
+    , MonadIO
 
-    -- | Re-exported from "System.IO" in __base__:
-    Handle,
-    IOMode (..),
-    withFile,
-    stdin,
-    stdout,
-    stderr,
-    hFlush,
-    unsafePerformIO,
+      -- ** from System.IO
 
-    -- * Exception handling
+      -- | Re-exported from "System.IO" in __base__:
+    , Handle
+    , IOMode (..)
+    , withFile
+    , stdin
+    , stdout
+    , stderr
+    , hFlush
+    , unsafePerformIO
 
-    Exception (..),
-    SomeException,
-) where
+      -- * Exception handling
+    , Exception (..)
+    , SomeException
+    ) where
 
-import Control.Exception (
-    Exception (..),
-    SomeException,
- )
+import Control.Exception
+    ( Exception (..)
+    , SomeException
+    )
 import Control.Monad.IO.Class (MonadIO, liftIO)
 import System.IO (Handle, IOMode (..), hFlush, stderr, stdin, stdout, withFile)
 import System.IO.Unsafe (unsafePerformIO)
diff --git a/lib/Core/System/External.hs b/lib/Core/System/External.hs
--- a/lib/Core/System/External.hs
+++ b/lib/Core/System/External.hs
@@ -3,5 +3,6 @@
 {- |
 Re-exports of dependencies from various external libraries.
 -}
-module Core.System.External (
+module Core.System.External
+    (
     ) where
diff --git a/lib/Core/System/Pretty.hs b/lib/Core/System/Pretty.hs
--- a/lib/Core/System/Pretty.hs
+++ b/lib/Core/System/Pretty.hs
@@ -1,47 +1,47 @@
 {-# OPTIONS_HADDOCK not-home #-}
 
 -- | Re-exports of combinators for use when building 'Core.Text.Utilities.Render' instances.
-module Core.System.Pretty (
-    -- * Pretty Printing
+module Core.System.Pretty
+    ( -- * Pretty Printing
 
-    -- ** from Data.Text.Prettyprint.Doc
+      -- ** from Data.Text.Prettyprint.Doc
 
-    -- | Re-exported from "Data.Text.Prettyprint.Doc" in __prettyprinter__ and "Data.Text.Prettyprint.Doc.Render.Terminal" in __prettyprinter-ansi-terminal__:
-    Doc,
-    Pretty (pretty),
-    dquote,
-    squote,
-    comma,
-    punctuate,
-    enclose,
-    lbracket,
-    rbracket,
-    (<+>),
-    lbrace,
-    rbrace,
-    lparen,
-    rparen,
-    emptyDoc,
-    sep,
-    hsep,
-    vsep,
-    fillCat,
-    fillSep,
-    flatAlt,
-    hcat,
-    vcat,
-    annotate,
-    unAnnotate,
-    line,
-    line',
-    softline,
-    softline',
-    hardline,
-    group,
-    hang,
-    indent,
-    nest,
-    concatWith,
-) where
+      -- | Re-exported from "Data.Text.Prettyprint.Doc" in __prettyprinter__ and "Data.Text.Prettyprint.Doc.Render.Terminal" in __prettyprinter-ansi-terminal__:
+      Doc
+    , Pretty (pretty)
+    , dquote
+    , squote
+    , comma
+    , punctuate
+    , enclose
+    , lbracket
+    , rbracket
+    , (<+>)
+    , lbrace
+    , rbrace
+    , lparen
+    , rparen
+    , emptyDoc
+    , sep
+    , hsep
+    , vsep
+    , fillCat
+    , fillSep
+    , flatAlt
+    , hcat
+    , vcat
+    , annotate
+    , unAnnotate
+    , line
+    , line'
+    , softline
+    , softline'
+    , hardline
+    , group
+    , hang
+    , indent
+    , nest
+    , concatWith
+    ) where
 
 import Prettyprinter
