diff --git a/purebred-email.cabal b/purebred-email.cabal
--- a/purebred-email.cabal
+++ b/purebred-email.cabal
@@ -1,8 +1,6 @@
--- Initial purebred-email.cabal generated by cabal init.  For further 
--- documentation, see http://haskell.org/cabal/users-guide/
-
+cabal-version:       2.2
 name:                purebred-email
-version:             0.4.2
+version:             0.4.3
 synopsis:            types and parser for email messages (including MIME)
 description:
   The purebred email library.  RFC 5322, MIME, etc.
@@ -21,7 +19,7 @@
   - Improve handling of dates
   - Probably lots of other things
 
-license:             AGPL-3
+license:             AGPL-3.0-or-later
 license-file:        LICENSE
 author:              Fraser Tweedale
 maintainer:          frase@frase.id.au
@@ -32,9 +30,8 @@
   README.rst
   test-vectors/*.eml
   tests/golden/*.golden
-cabal-version:       >=1.10
 tested-with:
-  GHC==8.4.4, GHC==8.6.5, GHC==8.8.3, GHC==8.10.1
+  GHC ==8.4.4 || ==8.6.5 || ==8.8.4 || ==8.10.4 || ==9.0.1
 
 homepage:            https://github.com/purebred-mua/purebred-email
 bug-reports:         https://github.com/purebred-mua/purebred-email/issues
@@ -46,7 +43,22 @@
   description: Build demonstration programs
   default: False
 
+common common
+  default-language: Haskell2010
+  ghc-options: -Wall -Wredundant-constraints
+
+  build-depends:
+    base >= 4.11 && < 5
+    , attoparsec >= 0.13 && < 0.14
+    , bytestring >= 0.10 && < 0.11
+    , case-insensitive >= 1.2 && < 1.3
+    , lens >= 4 && < 6
+    , semigroups >= 0.16
+    , text >= 1.2
+    , time >= 1.8
+
 library
+  import: common
   exposed-modules:
     Data.RFC5322
     , Data.RFC5322.Address.Text
@@ -65,27 +77,18 @@
     Data.MIME.Internal
   -- other-extensions:    
   build-depends:
-    base >= 4.11 && < 5
-    , attoparsec >= 0.13 && < 0.14
     , base64-bytestring >= 1 && < 2
-    , bytestring >= 0.10 && < 0.11
     , case-insensitive >= 1.2 && < 1.3
     , concise >= 0.1.0.1 && < 1
     , deepseq >= 1.4.2
-    , lens >= 4 && < 5
     , semigroupoids >= 5 && < 6
-    , semigroups >= 0.16
     , stringsearch >= 0.3
-    , text >= 1.2
-    , time
   hs-source-dirs:      src
   default-language:    Haskell2010
-  ghc-options: -Wall -Wredundant-constraints
 
 test-suite tests
+  import: common
   type: exitcode-stdio-1.0
-  default-language:    Haskell2010
-  ghc-options: -Wall -Wredundant-constraints
   hs-source-dirs: tests
   main-is: Test.hs
   other-modules:
@@ -97,16 +100,7 @@
     , Parser
     , Message
   build-depends:
-    base
-    , attoparsec
-    , bytestring
-    , lens
-    , text
     , purebred-email
-    , case-insensitive >= 1.2 && < 1.3
-    , semigroups >= 0.16
-    , time
-
     , tasty
     , tasty-hedgehog
     , tasty-quickcheck
@@ -117,15 +111,10 @@
     , quickcheck-instances
 
 executable purebred-email-parse
+  import: common
   if !flag(demos)
     buildable: False
-  default-language:    Haskell2010
   hs-source-dirs: tools
-  ghc-options: -Wall
   main-is: Parse.hs
   build-depends:
-    base
-    , attoparsec
-    , bytestring
-    , semigroups >= 0.16
     , purebred-email
diff --git a/src/Data/MIME.hs b/src/Data/MIME.hs
--- a/src/Data/MIME.hs
+++ b/src/Data/MIME.hs
@@ -5,7 +5,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE RankNTypes #-}
 {-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeSynonymInstances #-}
 
 {- |
 
@@ -51,6 +50,7 @@
   , transferDecoded
   , transferDecoded'
   , charsetDecoded
+  , charsetDecoded'
 
   -- ** Header processing
   , decodeEncodedWords
@@ -63,6 +63,7 @@
   , matchContentType
   , ctEq
   , parseContentType
+  , renderContentType
   , showContentType
   , mimeBoundary
 
@@ -79,6 +80,7 @@
   , dispositionType
   , filename
   , filenameParameter
+  , renderContentDisposition
 
   -- ** Mail creation
   -- *** Common use cases
@@ -106,10 +108,10 @@
   ) where
 
 import Control.Applicative
+import Data.Either (fromRight)
 import Data.Foldable (fold)
 import Data.List.NonEmpty (NonEmpty, fromList, intersperse)
 import Data.Maybe (fromMaybe, catMaybes)
-import Data.Semigroup ((<>))
 import Data.String (IsString(fromString))
 import GHC.Generics (Generic)
 
@@ -484,8 +486,8 @@
 matchContentType wantType wantSubtype (ContentType gotType gotSubtype _) =
   wantType == gotType && maybe True (== gotSubtype) wantSubtype
 
-printContentType :: ContentType -> B.ByteString
-printContentType (ContentType typ sub params) =
+renderContentType :: ContentType -> B.ByteString
+renderContentType (ContentType typ sub params) =
   CI.original typ <> "/" <> CI.original sub <> printParameters params
 
 printParameters :: Parameters -> B.ByteString
@@ -510,7 +512,7 @@
 
 -- | Rendered content type field value for displaying
 showContentType :: ContentType -> T.Text
-showContentType = decodeLenient . printContentType
+showContentType = decodeLenient . renderContentType
 
 instance HasParameters ContentType where
   parameters = ctParameters
@@ -649,7 +651,7 @@
     Just _ ->
       fromMaybe defaultContentType $ preview (ct . parsed parseContentType) s
 
-  sbt s b = set (at "Content-Type") (Just (printContentType b)) s
+  sbt s b = set (at "Content-Type") (Just (renderContentType b)) s
 
   ct = header "content-type"
   cte = contentTransferEncoding . to (`lookup` transferEncodings)
@@ -669,7 +671,7 @@
 dispositionType :: Lens' ContentDisposition DispositionType
 dispositionType f (ContentDisposition a b) =
   fmap (\a' -> ContentDisposition a' b) (f a)
-{-# ANN dispositionType ("HLint: ignore Avoid lambda" :: String) #-}
+{-# ANN dispositionType ("HLint: ignore Avoid lambda using `infix`" :: String) #-}
 
 dispositionParameters :: Lens' ContentDisposition Parameters
 dispositionParameters f (ContentDisposition a b) =
@@ -693,8 +695,9 @@
       | s == "inline" = Inline
       | otherwise = Attachment
 
-printContentDisposition :: ContentDisposition -> B.ByteString
-printContentDisposition (ContentDisposition typ params) =
+-- | Render the Content-Disposition value, including parameters.
+renderContentDisposition :: ContentDisposition -> B.ByteString
+renderContentDisposition (ContentDisposition typ params) =
   typStr <> printParameters params
   where
     typStr = case typ of Inline -> "inline" ; Attachment -> "attachment"
@@ -711,7 +714,7 @@
 contentDisposition :: HasHeaders a => Lens' a (Maybe ContentDisposition)
 contentDisposition = headers . at "Content-Disposition" . dimap
   (>>= either (const Nothing) Just . Data.RFC5322.parse parseContentDisposition)
-  (fmap . fmap $ printContentDisposition)
+  (fmap . fmap $ renderContentDisposition)
 
 -- | Traverse the value of the filename parameter (if present).
 --
@@ -837,13 +840,13 @@
 
 headerFrom :: HasHeaders a => CharsetLookup -> Lens' a [Mailbox]
 headerFrom charsets = headerSingleToList
-  (either (const []) id . parseOnly (mailboxList charsets))
+  (fromRight [] . parseOnly (mailboxList charsets))
   renderMailboxes
   "From"
 
 headerAddressList :: (HasHeaders a) => CI B.ByteString -> CharsetLookup -> Lens' a [Address]
 headerAddressList k charsets = headerSingleToList
-  (either (const []) id . parseOnly (addressList charsets))
+  (fromRight [] . parseOnly (addressList charsets))
   renderAddresses
   k
 
diff --git a/src/Data/MIME/Charset.hs b/src/Data/MIME/Charset.hs
--- a/src/Data/MIME/Charset.hs
+++ b/src/Data/MIME/Charset.hs
@@ -89,6 +89,13 @@
     -> ( forall p f. (Profunctor p, Contravariant f)
         => Optic' p f a (Either e (Decoded a)) )
 
+  -- | Structure with the encoded data replaced with 'Text' (monomorphic error type)
+  charsetDecoded'
+    :: CharsetLookup
+    -> ( forall p f. (Profunctor p, Contravariant f)
+        => Optic' p f a (Either CharsetError (Decoded a)) )
+  charsetDecoded' = charsetDecoded
+
   -- | Encode the data
   charsetEncode :: Decoded a -> a
 
diff --git a/src/Data/MIME/EncodedWord.hs b/src/Data/MIME/EncodedWord.hs
--- a/src/Data/MIME/EncodedWord.hs
+++ b/src/Data/MIME/EncodedWord.hs
@@ -19,7 +19,6 @@
   ) where
 
 import Control.Applicative ((<|>), liftA2, optional)
-import Data.Semigroup ((<>))
 import Data.Maybe (fromMaybe)
 import Data.Monoid (Sum(Sum), Any(Any))
 
@@ -71,7 +70,7 @@
   type Decoded TransferDecodedEncodedWord = T.Text
   charsetName = to (pure . _transDecWordCharset)
   charsetData = to _transDecWordText
-  charsetDecoded = charsetText
+  charsetDecoded charsets = charsetText charsets
 
   charsetEncode s =
     let
diff --git a/src/Data/MIME/Parameter.hs b/src/Data/MIME/Parameter.hs
--- a/src/Data/MIME/Parameter.hs
+++ b/src/Data/MIME/Parameter.hs
@@ -39,7 +39,7 @@
 import Control.Applicative ((<|>), optional)
 import Data.Foldable (fold)
 import Data.Functor (($>))
-import Data.Semigroup ((<>), Sum(..), Max(..))
+import Data.Semigroup (Sum(..), Max(..))
 import Data.String (IsString(..))
 import Data.Word (Word8)
 import Data.Void (Void)
diff --git a/src/Data/RFC5322.hs b/src/Data/RFC5322.hs
--- a/src/Data/RFC5322.hs
+++ b/src/Data/RFC5322.hs
@@ -117,7 +117,6 @@
 import Data.Foldable (fold)
 import Data.List (findIndex, intersperse)
 import Data.List.NonEmpty (intersperse)
-import Data.Semigroup ((<>))
 import Data.Word (Word8)
 import GHC.Generics (Generic)
 
@@ -376,7 +375,7 @@
 
 group :: CharsetLookup -> Parser Address
 group charsets =
-  Group <$> (displayName charsets) <* char8 ':'
+  Group <$> displayName charsets <* char8 ':'
         <*> mailboxList charsets <* char8 ';' <* optionalCFWS
 
 address :: CharsetLookup -> Parser Address
diff --git a/src/Data/RFC5322/Address/Text.hs b/src/Data/RFC5322/Address/Text.hs
--- a/src/Data/RFC5322/Address/Text.hs
+++ b/src/Data/RFC5322/Address/Text.hs
@@ -20,7 +20,6 @@
 
 import Control.Applicative ((<|>), optional)
 import Data.Foldable (fold)
-import Data.Semigroup ((<>))
 import Data.List (intersperse)
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
diff --git a/src/Data/RFC5322/Internal.hs b/src/Data/RFC5322/Internal.hs
--- a/src/Data/RFC5322/Internal.hs
+++ b/src/Data/RFC5322/Internal.hs
@@ -2,7 +2,6 @@
 {-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FunctionalDependencies #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
@@ -65,7 +64,6 @@
 import Data.Foldable (fold)
 import Data.Functor (($>))
 import Data.List.NonEmpty (NonEmpty, fromList, intersperse)
-import Data.Semigroup (Semigroup((<>)))
 import Data.Semigroup.Foldable (fold1)
 import qualified Data.Text as T
 import Data.Word (Word8)
diff --git a/tests/Headers.hs b/tests/Headers.hs
--- a/tests/Headers.hs
+++ b/tests/Headers.hs
@@ -5,7 +5,6 @@
 import Control.Lens
 import Data.List.NonEmpty (NonEmpty((:|)))
 import Data.String (IsString)
-import Data.Semigroup ((<>))
 import Data.Word (Word8)
 
 import qualified Data.ByteString as B
diff --git a/tools/Parse.hs b/tools/Parse.hs
--- a/tools/Parse.hs
+++ b/tools/Parse.hs
@@ -7,7 +7,6 @@
 -}
 
 import Data.Foldable (traverse_)
-import Data.Semigroup ((<>))
 import System.Environment (getArgs)
 
 import qualified Data.ByteString as B
