diff --git a/hw-prelude.cabal b/hw-prelude.cabal
--- a/hw-prelude.cabal
+++ b/hw-prelude.cabal
@@ -1,6 +1,6 @@
 cabal-version:          3.4
 name:                   hw-prelude
-version:                0.0.1.0
+version:                0.0.1.1
 synopsis:               Opinionated prelude library
 description:            Opinionated prelude library.
 license:                Apache-2.0
@@ -53,6 +53,7 @@
                         FlexibleContexts
                         FlexibleInstances
                         LambdaCase
+                        MultiWayIf
                         NoFieldSelectors
                         NoImplicitPrelude
                         OverloadedRecordDot
@@ -93,11 +94,15 @@
                         HaskellWorks.Error.Types
                         HaskellWorks.Error.Types.GenericError
                         HaskellWorks.Error.Types.TimedOut
+                        HaskellWorks.FilePath
                         HaskellWorks.IO.Network.NamedPipe
                         HaskellWorks.IO.Network.Port
                         HaskellWorks.IO.Network.Socket
                         HaskellWorks.IO.Process
+                        HaskellWorks.OS
                         HaskellWorks.Prelude
+                        HaskellWorks.Stack
+                        HaskellWorks.String
                         HaskellWorks.ToText
                         HaskellWorks.Unsafe
   hs-source-dirs:       src
diff --git a/src/HaskellWorks/Error/Types.hs b/src/HaskellWorks/Error/Types.hs
--- a/src/HaskellWorks/Error/Types.hs
+++ b/src/HaskellWorks/Error/Types.hs
@@ -1,6 +1,3 @@
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE TypeSynonymInstances  #-}
-
 module HaskellWorks.Error.Types (
     GenericError (..),
     TimedOut(..),
diff --git a/src/HaskellWorks/Error/Types/GenericError.hs b/src/HaskellWorks/Error/Types/GenericError.hs
--- a/src/HaskellWorks/Error/Types/GenericError.hs
+++ b/src/HaskellWorks/Error/Types/GenericError.hs
@@ -1,7 +1,3 @@
-{-# LANGUAGE DeriveGeneric         #-}
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE TypeSynonymInstances  #-}
-
 module HaskellWorks.Error.Types.GenericError (
     GenericError (..),
 ) where
diff --git a/src/HaskellWorks/Error/Types/TimedOut.hs b/src/HaskellWorks/Error/Types/TimedOut.hs
--- a/src/HaskellWorks/Error/Types/TimedOut.hs
+++ b/src/HaskellWorks/Error/Types/TimedOut.hs
@@ -1,7 +1,3 @@
-{-# LANGUAGE DeriveGeneric         #-}
-{-# LANGUAGE DuplicateRecordFields #-}
-{-# LANGUAGE TypeSynonymInstances  #-}
-
 module HaskellWorks.Error.Types.TimedOut (
     TimedOut (..),
 ) where
diff --git a/src/HaskellWorks/FilePath.hs b/src/HaskellWorks/FilePath.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/FilePath.hs
@@ -0,0 +1,18 @@
+module HaskellWorks.FilePath
+  ( exeSuffix,
+    addExeSuffix,
+  ) where
+
+import qualified HaskellWorks.OS as OS
+
+import qualified Data.List       as L
+import           Data.Monoid
+import           Data.String
+
+exeSuffix :: String
+exeSuffix = if OS.isWin32 then ".exe" else ""
+
+addExeSuffix :: String -> String
+addExeSuffix s = if ".exe" `L.isSuffixOf` s
+  then s
+  else s <> exeSuffix
diff --git a/src/HaskellWorks/IO/Network/Port.hs b/src/HaskellWorks/IO/Network/Port.hs
--- a/src/HaskellWorks/IO/Network/Port.hs
+++ b/src/HaskellWorks/IO/Network/Port.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE TypeApplications #-}
-
 module HaskellWorks.IO.Network.Port
   ( randomPort,
     reserveRandomPort,
diff --git a/src/HaskellWorks/IO/Network/Socket.hs b/src/HaskellWorks/IO/Network/Socket.hs
--- a/src/HaskellWorks/IO/Network/Socket.hs
+++ b/src/HaskellWorks/IO/Network/Socket.hs
@@ -1,6 +1,3 @@
-{-# LANGUAGE ScopedTypeVariables #-}
-{-# LANGUAGE TypeApplications    #-}
-
 module HaskellWorks.IO.Network.Socket
   ( doesSocketExist,
     isPortOpen,
diff --git a/src/HaskellWorks/IO/Win32/NamedPipe.hsc b/src/HaskellWorks/IO/Win32/NamedPipe.hsc
--- a/src/HaskellWorks/IO/Win32/NamedPipe.hsc
+++ b/src/HaskellWorks/IO/Win32/NamedPipe.hsc
@@ -1,8 +1,7 @@
 #include <fcntl.h>
 #include <windows.h>
 
-{-# LANGUAGE CPP                #-}
-{-# LANGUAGE MultiWayIf         #-}
+{-# LANGUAGE CPP #-}
 
 -- | For full details on the Windows named pipes API see
 -- <https://docs.microsoft.com/en-us/windows/desktop/ipc/named-pipes>
diff --git a/src/HaskellWorks/OS.hs b/src/HaskellWorks/OS.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/OS.hs
@@ -0,0 +1,12 @@
+module HaskellWorks.OS
+  ( isWin32,
+  ) where
+
+import           Data.Bool
+import           Data.Eq
+import           System.Info
+
+-- | Determine if the operating system is Windows.
+isWin32 :: Bool
+isWin32 = os == "mingw32"
+{-# INLINE isWin32 #-}
diff --git a/src/HaskellWorks/Stack.hs b/src/HaskellWorks/Stack.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/Stack.hs
@@ -0,0 +1,14 @@
+module HaskellWorks.Stack
+  ( callerModuleName,
+  ) where
+
+import           Data.Function
+import           Data.Maybe    (listToMaybe, maybe)
+import           Data.String
+import           Data.Tuple
+import           GHC.Stack     (HasCallStack, callStack, getCallStack,
+                                srcLocModule)
+
+-- | Get the module name of the caller.
+callerModuleName :: HasCallStack => String
+callerModuleName = maybe "<no-module>" (srcLocModule . snd) (listToMaybe (getCallStack callStack))
diff --git a/src/HaskellWorks/String.hs b/src/HaskellWorks/String.hs
new file mode 100644
--- /dev/null
+++ b/src/HaskellWorks/String.hs
@@ -0,0 +1,21 @@
+module HaskellWorks.String
+  ( ToString(..),
+  ) where
+
+import           Data.Function
+import           Data.String
+import           Data.Text      (Text)
+import qualified Data.Text      as T
+import qualified Data.Text.Lazy as LT
+
+class ToString a where
+  toString :: a -> String
+
+instance ToString String where
+  toString = id
+
+instance ToString Text where
+  toString = T.unpack
+
+instance ToString LT.Text where
+  toString = LT.unpack
