diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,3 @@
 # stack build artifacts
 .stack-work/
-
-# Using hpack, we don't store cabal files
-*.cabal
+stack.yaml.lock
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2013-2018, Dino Morelli <dino@ui3.info>
+Copyright (c) 2013, Dino Morelli <dino@ui3.info>
 
 Permission to use, copy, modify, and/or distribute this software
 for any purpose with or without fee is hereby granted, provided
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,19 +3,19 @@
 
 ## Synopsis
 
-Haskell shell script template (Haskell)
+Haskell shell script templates (Haskell)
 
 
 ## Description
 
-A template for writing shell scripts in Haskell. Contains some
-useful functions and examples of things commonly done in bash.
+Templates for writing shell scripts in Haskell. Includes some useful functions
+and examples of things commonly done in bash. And an example of using
+optparse-applicative in a shell script.
 
 
 ## Getting source
 
 - Download the cabalized source package [from Hackage](http://hackage.haskell.org/package/hscrtmpl)
-- Get the source with darcs: `$ git clone https://github.com/dino-/hscrtmpl.git`
 - Get the source with stack: `$ stack unpack hscrtmpl`
 - If you're just looking, [browse the source](https://github.com/dino-/hscrtmpl)
 
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/args-example.hs b/args-example.hs
new file mode 100644
--- /dev/null
+++ b/args-example.hs
@@ -0,0 +1,101 @@
+#! /usr/bin/env stack
+-- stack runghc --package ansi-wl-pprint --package heredoc --package optparse-applicative
+{-# LANGUAGE QuasiQuotes #-}
+
+-- If you need a specific resolver, do this:
+-- stack --resolver lts-16.11 runghc --package ...
+
+{-
+  This is a 'skeleton file' for writing shell scripts with Haskell that have
+  sophisticated arg parsing with optparse-applicative.
+
+  The idea here is to take a copy of this script and hack it to do what you
+  need quickly. Throw the rest out.
+
+  For more examples of doing common shell tasks, see the hscrtmpl.hs script
+  also in this project.
+
+
+  Dino Morelli <dino@ui3.info>
+  https://github.com/dino-/hscrtmpl
+  version: 2.0  2023-06-27
+-}
+
+import Options.Applicative
+import System.Environment ( getProgName )
+import Text.Heredoc ( here )
+import Text.PrettyPrint.ANSI.Leijen ( string )
+import Text.Printf ( printf )
+
+
+main :: IO ()
+main = do
+  opts <- parseOpts
+  print opts
+
+
+data Verbosity = Normal | Verbose
+  deriving Show  -- For debugging, possibly not needed
+
+data Options = Options
+  { optSwitch :: Bool
+  , optMaybeString :: Maybe String
+  , optFlag :: Verbosity
+  , optString :: String
+  , optArgument :: String
+  , optArguments :: [String]
+  }
+  deriving Show  -- For debugging, possibly not needed
+
+
+parseOpts :: IO Options
+parseOpts = do
+  pn <- getProgName
+  execParser $ info (parser <**> helper)
+    (  header (printf "%s - Argument parsing demo script" pn)
+    <> footer'
+    )
+
+
+-- For more help on building parsers: https://github.com/pcapriotti/optparse-applicative
+parser :: Parser Options
+parser = Options
+  <$> ( switch
+        (  long "switch" <> short 's'
+        <> help "A switch option"
+        )
+      )
+  <*> ( optional $ strOption
+        (  long "maybe-string" <> short 'm' <> metavar "STRING"
+        <> help "An optional string option"
+        )
+      )
+  <*> ( flag Normal Verbose
+        (  long "verbose" <> short 'v'
+        <> help "A flag option"
+        )
+      )
+  <*> ( strOption
+        (  long "string" <> short 'S' <> metavar "STRING"
+        <> help "A required string option"
+        )
+      )
+  <*> ( argument str
+        (  metavar "DIR"
+        <> help "A required single argument"
+        )
+      )
+  <*> some ( argument str
+        (  metavar "FILES..."
+        <> help "Multiple arguments, one or more required"
+        )
+      )
+
+
+footer' :: InfoMod a
+footer' = footerDoc . Just $ string content
+  where content = [here|OVERVIEW
+
+Put more info here about what this script is for and how it works
+
+v2.0  2023-06-27  Dino Morelli <dino@ui3.info>|]
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,30 @@
+2.0 (2023-06-28)
+
+  * Wrote a new args-example script
+  * Added a note about specifying the Stackage resolver
+  * Switched to modern cabal file instead of hpack
+  * Moved Stackage resolver up to 16.11 (ghc 8.8.3)
+  * Removed Setup.hs
+  * Added example of custom error message with lookupEnv
+  * Removed instructions for darcs
+  * Removed later copyright date
+  * Reorganized the functions in args-example.hs
+  * Updated cabal file and README to reflect multiple scripts
+
+
+1.8 (2021-09-05)
+
+  * Moved copyright up to 2021
+  * Added stack.yaml.lock to .gitignore
+  * Replace forSystem function
+
+
+1.7 (2019-08-22)
+
+  * Added sequence expression example
+  * Replaced a case statement with an if
+
+
 1.6 (2018-10-03)
 
   * Moved copyright date up to 2018
diff --git a/hscrtmpl.cabal b/hscrtmpl.cabal
--- a/hscrtmpl.cabal
+++ b/hscrtmpl.cabal
@@ -1,43 +1,63 @@
--- This file has been generated from package.yaml by hpack version 0.28.2.
---
--- see: https://github.com/sol/hpack
---
--- hash: 94422872b0ab311808f7510b45c6c5bb31ee6db2edcd9e2d75de1aa77c28bb29
+cabal-version: 2.2
 
 name:           hscrtmpl
-version:        1.6
-synopsis:       Haskell shell script template
-description:    A template for writing shell scripts in Haskell. Contains some useful functions and examples of things commonly done in bash.
+version:        2.0
+synopsis:       Haskell shell script templates
+description:    Templates for writing shell scripts in Haskell. Includes some useful functions and examples of things commonly done in bash. And an example of using optparse-applicative in a shell script.
 category:       Application, Console, Scripting
 homepage:       https://github.com/dino-/hscrtmpl#readme
 bug-reports:    https://github.com/dino-/hscrtmpl/issues
 author:         Dino Morelli
-maintainer:     dino@ui3.info
-copyright:      2018 Dino Morelli
+maintainer:     Dino Morelli <dino@ui3.info>
+copyright:      2013 Dino Morelli
 license:        ISC
 license-file:   LICENSE
 build-type:     Simple
-cabal-version:  >= 1.10
 extra-source-files:
-    .gitignore
-    changelog.md
-    package.yaml
-    README.md
-    stack.yaml
+  changelog.md
+  .gitignore
+  README.md
+  stack.yaml
 
 source-repository head
   type: git
   location: https://github.com/dino-/hscrtmpl
 
+-- Hey! These source files are *scripts*, you don't have to build them unless
+-- you want to have a binary!
+
+common lang
+  default-language: Haskell2010
+  other-modules:
+    Paths_hscrtmpl
+  autogen-modules:
+    Paths_hscrtmpl
+  build-depends:
+      process
+  ghc-options:
+    -fwarn-tabs
+    -Wall
+    -Wcompat
+    -Wincomplete-record-updates
+    -Wincomplete-uni-patterns
+    -Wredundant-constraints
+
 executable hscrtmpl
+  import: lang
   main-is: hscrtmpl.hs
-  other-modules:
-      Paths_hscrtmpl
-  ghc-options: -fwarn-tabs -Wall -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints
   build-depends:
       base >=3 && <5
     , directory
     , filepath
-    , process
+    -- , regex-compat
     , time
-  default-language: Haskell2010
+    -- , time-locale-compat
+
+executable args-example
+  import: lang
+  main-is: args-example.hs
+  build-depends:
+      base >=3 && <5
+    , ansi-wl-pprint
+    , heredoc
+    , optparse-applicative
diff --git a/hscrtmpl.hs b/hscrtmpl.hs
--- a/hscrtmpl.hs
+++ b/hscrtmpl.hs
@@ -1,6 +1,9 @@
 #! /usr/bin/env stack
 {- stack runghc -}
 
+-- If you need a specific resolver, do this:
+-- stack --resolver lts-7.8 runghc
+
 {- No stack? Use this #! instead of the two lines above:
 #! /usr/bin/env runhaskell
 -}
@@ -25,10 +28,12 @@
 
   Dino Morelli <dino@ui3.info>
   https://github.com/dino-/hscrtmpl
-  version: 1.6
+  version: 2.0  2023-06-27
 -}
 
 import Control.Monad
+import Data.List
+import Data.Maybe
 import Data.Time
 import Data.Time.Format ( defaultTimeLocale )
 -- Or use time-locale-compat for backwards compatibility with GHC < 7.10
@@ -70,8 +75,21 @@
    
 
   -- environment variables (System.Environment)
+
+  -- This will exit with a message and failure code if the variable doesn't exist!
   putStrLn =<< getEnv "SHELL"               -- echo $SHELL
 
+  -- To handle unset variables, use lookupEnv :: String -> IO (Maybe String)
+
+  -- someValue <- maybe                        someValue="${SOMEVAR:?Error: SOMEVAR not set}"
+  --    (die "Error: SOMEVAR is not set")
+  --    pure =<< lookupEnv "SOMEVAR"
+
+  someValue <- fromMaybe "default value"    -- someValue="${SOMEVAR:-default value}"
+     <$> lookupEnv "SOMEVAR"
+  putStrLn someValue
+
+
   -- arguments (System.Environment)
   --(arg1 : arg2 : _) <- getArgs              -- arg1=$1 ; arg2=$2
 
@@ -79,6 +97,9 @@
   printf "The %s is %d\n"                   -- S="answer" ; D=42
     "answer" (42::Int)                     -- echo "The $S is $D"
 
+  -- sequence expressions
+  -- [1..10]                                   -- {1..10}
+
   -- execution, exit code (System.Process)
   ec <- system "ls -l"                      -- ls -l
   print ec                                  -- echo $?
@@ -147,14 +168,18 @@
 exitBool = exitWith . toExitCode
 
 
-{- This is similar to the for/in/do/done construct in bash with an
-   important difference. This action evaluates to ExitFailure 1 if
-   *any* of the command executions fails. In bash you only get the
-   exit code of the last one.
+{- This is similar to the for/in/do/done construct in bash with an important
+   difference. This action evaluates to the "worst" exit code of all IO actions
+   that are passed after they are evaluated.
 -}
-forSystem :: [String] -> IO ExitCode
-forSystem cs = do
-  ecs <- mapM system cs
-  return $ case all (== ExitSuccess) ecs of
-    True  -> ExitSuccess
-    False -> ExitFailure 1
+sequenceWorstEC :: [IO ExitCode] -> IO ExitCode
+sequenceWorstEC as =
+  fromMaybe ExitSuccess . listToMaybe . reverse . sort <$> sequence as
+
+
+{- This is similar to the for/in/do/done construct in bash with an important
+   difference. This action evaluates to the "worst" exit code of all shell
+   commands that are passed after they are evaluated.
+-}
+systemWorstEC :: [String] -> IO ExitCode
+systemWorstEC = sequenceWorstEC . map system
diff --git a/package.yaml b/package.yaml
deleted file mode 100644
--- a/package.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: hscrtmpl
-version: '1.6'
-synopsis: Haskell shell script template
-description: A template for writing shell scripts in Haskell. Contains some useful functions and examples of things commonly done in bash.
-license: ISC
-author: Dino Morelli
-maintainer: dino@ui3.info
-copyright: 2018 Dino Morelli
-category: Application, Console, Scripting
-extra-source-files:
-- changelog.md
-- .gitignore
-- README.md
-- package.yaml
-- stack.yaml
-
-github: dino-/hscrtmpl
-
-ghc-options:
-- -fwarn-tabs
-- -Wall
-- -Wcompat
-- -Wincomplete-record-updates
-- -Wincomplete-uni-patterns
-- -Wredundant-constraints
-
-# Hey! hscrtmpl.hs is a *script*, you don't have to build it unless
-# you want to have a binary!
-executables:
-  hscrtmpl:
-    main: hscrtmpl.hs
-    dependencies:
-      - base >=3 && <5
-      - directory
-      - filepath
-      - process
-      # - regex-compat
-      - time
-      # - time-locale-compat
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,4 +1,4 @@
-resolver: lts-12.11
+resolver: lts-16.11
 
 packages:
 - '.'
