diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
 Changelog for the `operational` package
 ---------------------------------------
 
+**0.2.4.0** -- Feature release.
+
+* Update to build with GHC 9.0.1.
+* Add utility functions `interpretWithMonadT`, `unviewT` and `mapInstr`
+* Add utility `Functor`, `Applicative`, and `Monad` instances for `ProgramViewT` type.
+
 **0.2.3.5** -- Maintenance release.
 
 * Update references to other packages.
diff --git a/doc/examples/TicTacToe.hs b/doc/examples/TicTacToe.hs
--- a/doc/examples/TicTacToe.hs
+++ b/doc/examples/TicTacToe.hs
@@ -20,7 +20,7 @@
 import Control.Monad.State
 
 import Data.Either
-import Data.List
+import Data.List (transpose, intersperse)
 
     -- external libraries needed
 import System.Random
diff --git a/operational.cabal b/operational.cabal
--- a/operational.cabal
+++ b/operational.cabal
@@ -1,5 +1,5 @@
 Name:               operational
-Version:            0.2.3.5
+Version:            0.2.4.0
 Synopsis:           Implementation of difficult monads made easy
                     with operational semantics.
 Description:
diff --git a/src/Control/Monad/Operational.hs b/src/Control/Monad/Operational.hs
--- a/src/Control/Monad/Operational.hs
+++ b/src/Control/Monad/Operational.hs
@@ -5,25 +5,25 @@
 module Control.Monad.Operational (
     -- * Synopsis
     -- $synopsis
-    
+
     -- * Overview
     -- $intro
-    
+
     -- * Monad
     Program, singleton, ProgramView, view,
     -- $example
     interpretWithMonad,
-    
+
     -- * Monad transformer
     ProgramT, ProgramViewT(..), viewT,
     -- $exampleT
-    liftProgram,
-    
+    liftProgram, mapInstr,
+    unviewT, interpretWithMonadT,
+
     ) where
 
 import Control.Monad.Identity
 import Control.Monad.Trans
-import Control.Applicative
 
     -- mtl  classes to instantiate.
     -- Those commented out cannot be instantiated. For reasons see below.
@@ -95,7 +95,7 @@
 you can wait for the user to return input as shown,
 or you store the continuation @k@ and retrieve it when
 your web application receives another HTTP request,
-or you can keep a log of all user inputs on the client side an replay them,
+or you can keep a log of all user inputs on the client side and replay them,
 and so on. Moreover, you can implement different @run@ functions
 for one and the same custom monad, which is useful for testing.
 Also note that the result type of the @run@ function does not need to
@@ -120,9 +120,9 @@
     i.e. sequences of primitive instructions.
 
     * The /primitive instructions/ are given by the type constructor @instr :: * -> *@.
-    
+
     * @a@ is the return type of a program.
-    
+
     @'Program' instr@ is always a monad and
     automatically obeys the monad laws.
 -}
@@ -144,7 +144,7 @@
 --
 -- This function can be useful if you are mainly interested in
 -- mapping a 'Program' to different standard monads, like the state monad.
--- For implementing a truly custom monad, 
+-- For implementing a truly custom monad,
 -- you should write your interpreter directly with 'view' instead.
 interpretWithMonad :: forall instr m b.
     Monad m => (forall a. instr a -> m a) -> (Program instr b -> m b)
@@ -187,11 +187,11 @@
     i.e. sequences of primitive instructions and actions from the base monad.
 
     * The /primitive instructions/ are given by the type constructor @instr :: * -> *@.
-    
+
     * @m@ is the base monad, embedded with 'lift'.
 
     * @a@ is the return type of a program.
-    
+
     @'ProgramT' instr m@ is a monad transformer and
     automatically obeys both the monad and the lifting laws.
 -}
@@ -234,6 +234,19 @@
            -> (b -> ProgramT instr m a)
            -> ProgramViewT instr m a
 
+instance Monad m => Functor (ProgramViewT instr m) where
+    fmap f (Return a) = Return $ f a
+    fmap f (instr :>>= cont) = instr :>>= (fmap f . cont)
+
+instance Monad m => Applicative (ProgramViewT instr m) where
+    pure = return
+    (<*>) = ap
+
+instance Monad m => Monad (ProgramViewT instr m) where
+    return = Return
+    Return a >>= cont = cont a
+    (instr :>>= cont1) >>= cont2 = instr :>>= (cont1 >=> unviewT . cont2)
+
 -- | View function for inspecting the first instruction.
 viewT :: Monad m => ProgramT instr m a -> m (ProgramViewT instr m a)
 viewT (Lift m)                = m >>= return . Return
@@ -245,7 +258,7 @@
 {-| Lift a plain sequence of instructions to a sequence
     of instructions over a monad 'm'.
     This is the counterpart of the 'lift' function from 'MonadTrans'.
-    
+
     It can be defined as follows:
 
 @
@@ -255,7 +268,7 @@
         eval (Return a) = return a
         eval (i :>>= k) = singleton i >>= liftProgram . k
 @
-    
+
 -}
 liftProgram :: Monad m => Program instr a -> ProgramT instr m a
 liftProgram (Lift m)     = return (runIdentity m)
@@ -263,6 +276,47 @@
 liftProgram (Instr i)    = Instr i
 
 
+-- | Utility function that extends
+-- a given interpretation of instructions as monadic actions
+-- to an interpration of 'ProgramT's as monadic actions.
+--
+-- Ideally, you would not use another monad,
+-- but write a custom interpreter directly with `viewT`.
+-- See the remark at 'interpretWithMonad'.
+interpretWithMonadT :: Monad m => (forall x . instr x -> m x) -> ProgramT instr m a -> m a
+interpretWithMonadT interpreter = go
+  where
+    go program = do
+      firstInstruction <- viewT program
+      case firstInstruction of
+        Return a -> return a
+        instruction :>>= continuation -> interpreter instruction >>= (go . continuation)
+
+-- | Utilitiy function for mapping a 'ProgramViewT' back into a 'ProgramT'.
+-- 
+-- Semantically, the function 'unviewT' is an inverse of 'viewT',
+-- e.g. we have
+--
+-- @
+--   viewT (singleton i) >>= unviewT = return (singleton i)
+-- @
+unviewT :: Monad m => ProgramViewT instr m a -> ProgramT instr m a
+unviewT (Return a) = return a
+unviewT (instruction :>>= continuation) =
+    (Instr instruction) `Bind` continuation
+
+-- | Extend a mapping of instructions to a mapping of 'ProgramT'.
+mapInstr ::
+    forall instr1 instr2 m a . Monad m
+    => (forall x . instr1 x -> instr2 x)
+    -> ProgramT instr1 m a -> ProgramT instr2 m a
+mapInstr f = go
+    where
+        go :: forall x. ProgramT instr1 m x -> ProgramT instr2 m x
+        go (Lift action) = Lift action
+        go (Bind action continuation) = Bind (go action) (go . continuation)
+        go (Instr instruction) = Instr $ f instruction
+
 {- $exampleT
 
 /Example usage/
@@ -290,11 +344,11 @@
 
 {------------------------------------------------------------------------------
     mtl instances
-    
+
   * All of these instances need UndecidableInstances,
     because they do not satisfy the coverage condition.
     Most of the instance in the  mtl  package itself have the same issue.
-    
+
   * Lifting algebraic operations is easy,
     lifting control operations is more elaborate, but sometimes possible.
     See the design notes in  `doc/design.md`.
@@ -308,8 +362,7 @@
 
 instance (MonadReader r m) => MonadReader r (ProgramT instr m) where
     ask = lift ask
-    
+
     local r (Lift m)     = Lift (local r m)
     local r (m `Bind` k) = local r m `Bind` (local r . k)
     local _ (Instr i)    = Instr i
-
