diff --git a/ascii-superset.cabal b/ascii-superset.cabal
--- a/ascii-superset.cabal
+++ b/ascii-superset.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: ascii-superset
-version: 1.2.5.0
+version: 1.2.6.0
 synopsis: Representing ASCII with refined supersets
 category: Data, Text
 
@@ -54,7 +54,6 @@
 
 library
     import: base
-    ghc-options: -fno-warn-unused-imports
     hs-source-dirs: library
 
     default-extensions:
@@ -71,9 +70,13 @@
     exposed-modules:
         ASCII.CaseRefinement
         ASCII.Superset
+        ASCII.SupersetConversion
         ASCII.Isomorphism
         ASCII.Refinement
         ASCII.Lift
+
+    other-modules:
+        ASCII.Refinement.Internal
 
 test-suite test-ascii-superset
     import: base
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,13 @@
+### 1.2.6.0 (2023-02-07)
+
+New module `ASCII.SupersetConversion` containing a new class,
+`StringSupersetConversion`, and the following function:
+
+```haskell
+convertRefinedString ::
+    StringSupersetConversion a b => ASCII a -> ASCII b
+```
+
 ### 1.2.5.0 (2023-01-06)
 
 New in `ASCII.CaseRefinement`:
diff --git a/library/ASCII/CaseRefinement.hs b/library/ASCII/CaseRefinement.hs
--- a/library/ASCII/CaseRefinement.hs
+++ b/library/ASCII/CaseRefinement.hs
@@ -12,27 +12,25 @@
 
 import ASCII.Case (Case (..))
 import ASCII.Caseless (CaselessChar)
-import {-# source #-} ASCII.Refinement (ASCII)
+import {-# source #-} ASCII.Refinement.Internal (ASCII)
 import ASCII.Superset (CharSuperset, StringSuperset)
 
 import qualified ASCII.Case as Case
 import qualified ASCII.Caseless as Caseless
 import qualified ASCII.Char as ASCII
 import qualified ASCII.Superset as S
-import {-# source #-} qualified ASCII.Refinement as Refinement
+import {-# source #-} qualified ASCII.Refinement.Internal as Refinement
 
 import Control.Monad (guard)
-import Data.Bool (Bool (..))
 import Data.Data (Data, Typeable)
 import Data.Eq (Eq)
 import Data.Foldable (any)
-import Data.Function (id, ($), (.))
+import Data.Function (($), (.))
 import Data.Hashable (Hashable)
 import Data.Maybe (Maybe (..))
 import Data.Monoid (Monoid)
 import Data.Ord (Ord, (>))
 import Data.Semigroup (Semigroup)
-import Data.Traversable (traverse)
 import GHC.Generics (Generic)
 import Prelude (succ)
 import Text.Show (Show, showList, showParen, showString, showsPrec)
diff --git a/library/ASCII/Lift.hs b/library/ASCII/Lift.hs
--- a/library/ASCII/Lift.hs
+++ b/library/ASCII/Lift.hs
@@ -7,19 +7,14 @@
 @ -}
 module ASCII.Lift (Lift (..)) where
 
-import ASCII.Case (Case (..))
 import ASCII.CaseRefinement (ASCII'case)
 import ASCII.Char (Char)
-import ASCII.Refinement (ASCII)
+import ASCII.Refinement.Internal (ASCII)
 import ASCII.Superset (CharSuperset, StringSuperset)
 
 import qualified ASCII.CaseRefinement as CaseRefinement
-import qualified ASCII.Refinement as Refinement
+import qualified ASCII.Refinement.Internal as Refinement
 import qualified ASCII.Superset as S
-
-import qualified Prelude
-
-import Data.Function ((.))
 
 {-| Embedding of one character set within another
 
diff --git a/library/ASCII/Refinement.hs b/library/ASCII/Refinement.hs
--- a/library/ASCII/Refinement.hs
+++ b/library/ASCII/Refinement.hs
@@ -2,166 +2,8 @@
   (
     {- * ASCII type constructor -} ASCII, lift, asciiUnsafe,
     {- * Character functions -} validateChar, fromChar, toChar, substituteChar, asChar,
-    {- * String functions -} validateString, fromCharList, toCharList, substituteString, mapChars
+    {- * String functions -} validateString, fromCharList, toCharList, substituteString, mapChars,
   )
   where
 
-import qualified ASCII.Char as ASCII
-import qualified ASCII.Isomorphism as I
-import qualified ASCII.Superset as S
-
-import ASCII.CaseRefinement (KnownCase (..), ASCII'case, ASCII'upper, ASCII'lower)
-import Data.Data (Data)
-import Data.Eq (Eq)
-import Data.Function (id, ($), (.))
-import Data.Hashable (Hashable)
-import Data.List (map)
-import Data.Maybe (Maybe (..))
-import Data.Monoid (Monoid)
-import Data.Ord (Ord, (>))
-import Data.Semigroup (Semigroup)
-import GHC.Generics (Generic)
-import Prelude (succ)
-
-import qualified Data.Bool as Bool
-import qualified Text.Show as Show
-
-{-| This type constructor indicates that a value from some ASCII superset
-    is valid ASCII
-
-The type parameter is the ASCII superset, which should be a type with an
-instance of either 'CharSuperset' or 'StringSuperset'.
-
-For example, whereas a 'Data.Text.Text' value may contain a combination of ASCII
-and non-ASCII characters, a value of type @'ASCII' 'Data.Text.Text'@ may contain
-only ASCII characters. -}
-newtype ASCII superset = ASCII_Unsafe { lift :: superset }
-
-deriving stock instance Eq superset => Eq (ASCII superset)
-
-deriving stock instance Ord superset => Ord (ASCII superset)
-
-deriving newtype instance Hashable superset => Hashable (ASCII superset)
-
-deriving newtype instance Semigroup superset => Semigroup (ASCII superset)
-
-deriving newtype instance Monoid superset => Monoid (ASCII superset)
-
-deriving stock instance Data superset => Data (ASCII superset)
-
-deriving stock instance Generic (ASCII superset)
-
-instance Show.Show superset => Show.Show (ASCII superset) where
-    showsPrec d x = Show.showParen (d > app_prec) $
-        Show.showString "asciiUnsafe " . Show.showsPrec (succ app_prec) (lift x)
-      where app_prec = 10
-
-    showList x = Show.showString "asciiUnsafe " . Show.showList (map lift x)
-
-instance S.ToCaselessChar char => S.ToCaselessChar (ASCII char) where
-    isAsciiCaselessChar _ = Bool.True
-    toCaselessCharUnsafe = S.toCaselessCharUnsafe . lift
-
-instance S.CharSuperset char => S.ToChar (ASCII char) where
-    isAsciiChar _ = Bool.True
-    toCharUnsafe = S.toCharUnsafe . lift
-
-instance S.CharSuperset char => S.FromChar (ASCII char) where
-    fromChar = asciiUnsafe . S.fromChar
-
-instance S.CharSuperset char => S.CharSuperset (ASCII char) where
-    toCaseChar c = asciiUnsafe . S.toCaseChar c . lift
-
-instance S.CharSuperset char => I.CharIso (ASCII char) where
-    toChar = S.toCharUnsafe
-
-instance S.ToCaselessString string => S.ToCaselessString (ASCII string) where
-    isAsciiCaselessString _ = Bool.True
-    toCaselessCharListUnsafe = S.toCaselessCharListUnsafe . lift
-    toCaselessCharListSub = S.toCaselessCharListSub . lift
-
-instance S.ToString string => S.ToString (ASCII string) where
-    isAsciiString _ = Bool.True
-    toCharListUnsafe = S.toCharListUnsafe . lift
-    toCharListSub = S.toCharListUnsafe . lift
-
-instance S.FromString string => S.FromString (ASCII string) where
-    fromCharList = asciiUnsafe . S.fromCharList
-
-instance S.StringSuperset string => S.StringSuperset (ASCII string) where
-    substituteString = id
-    toCaseString c = asciiUnsafe . S.toCaseString c . lift
-
-instance S.StringSuperset string => I.StringIso (ASCII string) where
-    toCharList = S.toCharListUnsafe
-    mapChars = S.mapCharsUnsafe
-
-{-| Change the type of an ASCII superset value that is known to be valid ASCII
-
-This is "unsafe" because this assertion is unchecked, so this function is capable
-of producing an invalid 'ASCII' value. -}
-asciiUnsafe :: superset -> ASCII superset
-asciiUnsafe = ASCII_Unsafe
-
-{-|
-
-@
-(map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)])
-    == [Nothing, Just (asciiUnsafe 65), Just (asciiUnsafe 97), Nothing]
-@ -}
-validateChar :: S.CharSuperset superset => superset -> Maybe (ASCII superset)
-validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing
-
-substituteChar :: S.CharSuperset superset => superset -> ASCII superset
-substituteChar x = if S.isAsciiChar x then asciiUnsafe x else fromChar ASCII.Substitute
-
-fromChar :: S.CharSuperset superset => ASCII.Char -> ASCII superset
-fromChar = asciiUnsafe . S.fromChar
-
-toChar :: S.CharSuperset superset => ASCII superset -> ASCII.Char
-toChar = S.toCharUnsafe . lift
-
-{-|
-
-@
-fromCharList [CapitalLetterH, SmallLetterI, ExclamationMark]
-    == (asciiUnsafe "Hi!" :: ASCII Text)
-@ -}
-fromCharList :: S.StringSuperset superset => [ASCII.Char] -> ASCII superset
-fromCharList = asciiUnsafe . S.fromCharList
-
-{-|
-
-@
-toCharList (substituteString \"Piñata" :: ASCII Text) ==
-    [CapitalLetterP, SmallLetterI, Substitute, SmallLetterA, SmallLetterT, SmallLetterA]
-@ -}
-toCharList :: S.StringSuperset superset => ASCII superset -> [ASCII.Char]
-toCharList = S.toCharListUnsafe . lift
-
-{-| Forces a string from a larger character set into ASCII by using the
-    'ASCII.Substitute' character in place of any non-ASCII characters
-
-@
-(substituteString \"Cristóbal" :: ASCII Text) == asciiUnsafe "Crist\SUBbal"
-@ -}
-substituteString :: S.StringSuperset superset => superset -> ASCII superset
-substituteString = asciiUnsafe . S.substituteString
-
-{-|
-
-@
-(map validateString [\"Hello", \"Cristóbal"] :: [Maybe (ASCII Text)])
-    == [Just (asciiUnsafe \"Hello"), Nothing]
-
-(map validateString [\"Hello", \"Cristóbal"] :: [Maybe (ASCII String)])
-    == [Just (asciiUnsafe \"Hello"), Nothing]
-@ -}
-validateString :: S.StringSuperset superset => superset -> Maybe (ASCII superset)
-validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing
-
-asChar :: S.CharSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset
-asChar f = asciiUnsafe . S.asCharUnsafe f . lift
-
-mapChars :: S.StringSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset
-mapChars f = asciiUnsafe . S.mapCharsUnsafe f . lift
+import ASCII.Refinement.Internal
diff --git a/library/ASCII/Refinement.hs-boot b/library/ASCII/Refinement.hs-boot
deleted file mode 100644
--- a/library/ASCII/Refinement.hs-boot
+++ /dev/null
@@ -1,7 +0,0 @@
-module ASCII.Refinement where
-
-data ASCII superset
-
-lift :: ASCII superset -> superset
-
-asciiUnsafe :: superset -> ASCII superset
diff --git a/library/ASCII/Refinement/Internal.hs b/library/ASCII/Refinement/Internal.hs
new file mode 100644
--- /dev/null
+++ b/library/ASCII/Refinement/Internal.hs
@@ -0,0 +1,166 @@
+module ASCII.Refinement.Internal where
+
+import qualified ASCII.Char as ASCII
+import qualified ASCII.Isomorphism as I
+import qualified ASCII.Superset as S
+
+import {-# source #-} ASCII.SupersetConversion (StringSupersetConversion)
+import Data.Data (Data)
+import Data.Eq (Eq)
+import Data.Function (id, ($), (.))
+import Data.Hashable (Hashable)
+import Data.List (map)
+import Data.Maybe (Maybe (..))
+import Data.Monoid (Monoid)
+import Data.Ord (Ord, (>))
+import Data.Semigroup (Semigroup)
+import GHC.Generics (Generic)
+import Prelude (succ)
+
+import qualified Data.Bool as Bool
+import qualified Text.Show as Show
+import {-# source #-} qualified ASCII.SupersetConversion as SupersetConversion
+
+{-| This type constructor indicates that a value from some ASCII superset
+    is valid ASCII
+
+The type parameter is the ASCII superset, which should be a type with an
+instance of either 'CharSuperset' or 'StringSuperset'.
+
+For example, whereas a 'Data.Text.Text' value may contain a combination of ASCII
+and non-ASCII characters, a value of type @'ASCII' 'Data.Text.Text'@ may contain
+only ASCII characters. -}
+newtype ASCII superset = ASCII_Unsafe { lift :: superset }
+
+deriving stock instance Eq superset => Eq (ASCII superset)
+
+deriving stock instance Ord superset => Ord (ASCII superset)
+
+deriving newtype instance Hashable superset => Hashable (ASCII superset)
+
+deriving newtype instance Semigroup superset => Semigroup (ASCII superset)
+
+deriving newtype instance Monoid superset => Monoid (ASCII superset)
+
+deriving stock instance Data superset => Data (ASCII superset)
+
+deriving stock instance Generic (ASCII superset)
+
+instance Show.Show superset => Show.Show (ASCII superset) where
+    showsPrec d x = Show.showParen (d > app_prec) $
+        Show.showString "asciiUnsafe " . Show.showsPrec (succ app_prec) (lift x)
+      where app_prec = 10
+
+    showList x = Show.showString "asciiUnsafe " . Show.showList (map lift x)
+
+instance S.ToCaselessChar char => S.ToCaselessChar (ASCII char) where
+    isAsciiCaselessChar _ = Bool.True
+    toCaselessCharUnsafe = S.toCaselessCharUnsafe . lift
+
+instance S.CharSuperset char => S.ToChar (ASCII char) where
+    isAsciiChar _ = Bool.True
+    toCharUnsafe = S.toCharUnsafe . lift
+
+instance S.CharSuperset char => S.FromChar (ASCII char) where
+    fromChar = asciiUnsafe . S.fromChar
+
+instance S.CharSuperset char => S.CharSuperset (ASCII char) where
+    toCaseChar c = asciiUnsafe . S.toCaseChar c . lift
+
+instance S.CharSuperset char => I.CharIso (ASCII char) where
+    toChar = S.toCharUnsafe
+
+instance S.ToCaselessString string => S.ToCaselessString (ASCII string) where
+    isAsciiCaselessString _ = Bool.True
+    toCaselessCharListUnsafe = S.toCaselessCharListUnsafe . lift
+    toCaselessCharListSub = S.toCaselessCharListSub . lift
+
+instance S.ToString string => S.ToString (ASCII string) where
+    isAsciiString _ = Bool.True
+    toCharListUnsafe = S.toCharListUnsafe . lift
+    toCharListSub = S.toCharListUnsafe . lift
+
+instance S.FromString string => S.FromString (ASCII string) where
+    fromCharList = asciiUnsafe . S.fromCharList
+
+instance S.StringSuperset string => S.StringSuperset (ASCII string) where
+    substituteString = id
+    toCaseString c = asciiUnsafe . S.toCaseString c . lift
+
+instance S.StringSuperset string => I.StringIso (ASCII string) where
+    toCharList = S.toCharListUnsafe
+    mapChars = S.mapCharsUnsafe
+
+{-| Change the type of an ASCII superset value that is known to be valid ASCII
+
+This is "unsafe" because this assertion is unchecked, so this function is capable
+of producing an invalid 'ASCII' value. -}
+asciiUnsafe :: superset -> ASCII superset
+asciiUnsafe = ASCII_Unsafe
+
+{-|
+
+@
+(map validateChar [-1, 65, 97, 128] :: [Maybe (ASCII Int)])
+    == [Nothing, Just (asciiUnsafe 65), Just (asciiUnsafe 97), Nothing]
+@ -}
+validateChar :: S.CharSuperset superset => superset -> Maybe (ASCII superset)
+validateChar x = if S.isAsciiChar x then Just (asciiUnsafe x) else Nothing
+
+substituteChar :: S.CharSuperset superset => superset -> ASCII superset
+substituteChar x = if S.isAsciiChar x then asciiUnsafe x else fromChar ASCII.Substitute
+
+fromChar :: S.CharSuperset superset => ASCII.Char -> ASCII superset
+fromChar = asciiUnsafe . S.fromChar
+
+toChar :: S.CharSuperset superset => ASCII superset -> ASCII.Char
+toChar = S.toCharUnsafe . lift
+
+{-|
+
+@
+fromCharList [CapitalLetterH, SmallLetterI, ExclamationMark]
+    == (asciiUnsafe "Hi!" :: ASCII Text)
+@ -}
+fromCharList :: S.StringSuperset superset => [ASCII.Char] -> ASCII superset
+fromCharList = asciiUnsafe . S.fromCharList
+
+{-|
+
+@
+toCharList (substituteString \"Piñata" :: ASCII Text) ==
+    [CapitalLetterP, SmallLetterI, Substitute, SmallLetterA, SmallLetterT, SmallLetterA]
+@ -}
+toCharList :: S.StringSuperset superset => ASCII superset -> [ASCII.Char]
+toCharList = S.toCharListUnsafe . lift
+
+{-| Forces a string from a larger character set into ASCII by using the
+    'ASCII.Substitute' character in place of any non-ASCII characters
+
+@
+(substituteString \"Cristóbal" :: ASCII Text) == asciiUnsafe "Crist\SUBbal"
+@ -}
+substituteString :: S.StringSuperset superset => superset -> ASCII superset
+substituteString = asciiUnsafe . S.substituteString
+
+{-|
+
+@
+(map validateString [\"Hello", \"Cristóbal"] :: [Maybe (ASCII Text)])
+    == [Just (asciiUnsafe \"Hello"), Nothing]
+
+(map validateString [\"Hello", \"Cristóbal"] :: [Maybe (ASCII String)])
+    == [Just (asciiUnsafe \"Hello"), Nothing]
+@ -}
+validateString :: S.StringSuperset superset => superset -> Maybe (ASCII superset)
+validateString x = if S.isAsciiString x then Just (asciiUnsafe x) else Nothing
+
+asChar :: S.CharSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset
+asChar f = asciiUnsafe . S.asCharUnsafe f . lift
+
+mapChars :: S.StringSuperset superset => (ASCII.Char -> ASCII.Char) -> ASCII superset -> ASCII superset
+mapChars f = asciiUnsafe . S.mapCharsUnsafe f . lift
+
+{-| For example, this function can convert @ASCII ByteString@ to @ASCII Text@ and vice versa -}
+convertRefinedString :: StringSupersetConversion a b => ASCII a -> ASCII b
+convertRefinedString (ASCII_Unsafe x) = ASCII_Unsafe (SupersetConversion.convertStringUnsafe x)
diff --git a/library/ASCII/Refinement/Internal.hs-boot b/library/ASCII/Refinement/Internal.hs-boot
new file mode 100644
--- /dev/null
+++ b/library/ASCII/Refinement/Internal.hs-boot
@@ -0,0 +1,11 @@
+module ASCII.Refinement.Internal where
+
+import {-# source #-} ASCII.SupersetConversion (StringSupersetConversion)
+
+data ASCII superset
+
+lift :: ASCII superset -> superset
+
+asciiUnsafe :: superset -> ASCII superset
+
+convertRefinedString :: StringSupersetConversion a b => ASCII a -> ASCII b
diff --git a/library/ASCII/SupersetConversion.hs b/library/ASCII/SupersetConversion.hs
new file mode 100644
--- /dev/null
+++ b/library/ASCII/SupersetConversion.hs
@@ -0,0 +1,73 @@
+module ASCII.SupersetConversion
+  (
+    {- * Class -} StringSupersetConversion (..),
+    {- * Utilities -} convertRefinedString,
+  )
+  where
+
+import ASCII.Superset
+
+import {-# source #-} ASCII.Refinement.Internal (convertRefinedString)
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as BS.Char8
+import qualified Data.ByteString.Lazy as LBS
+import qualified Data.ByteString.Lazy.Char8 as LBS.Char8
+import qualified Data.Char as Unicode
+import qualified Data.Text as T
+import qualified Data.Text.Encoding as T
+import qualified Data.Text.Lazy as LT
+import qualified Data.Text.Lazy.Encoding as LT
+
+class (StringSuperset a, StringSuperset b) => StringSupersetConversion a b where
+    convertStringUnsafe :: a -> b
+
+instance StringSupersetConversion T.Text BS.ByteString where
+    convertStringUnsafe = T.encodeUtf8
+
+instance StringSupersetConversion BS.ByteString T.Text where
+    convertStringUnsafe = T.decodeUtf8
+
+instance StringSupersetConversion LT.Text LBS.ByteString where
+    convertStringUnsafe = LT.encodeUtf8
+
+instance StringSupersetConversion LBS.ByteString LT.Text where
+    convertStringUnsafe = LT.decodeUtf8
+
+instance StringSupersetConversion T.Text LT.Text where
+    convertStringUnsafe = LT.fromStrict
+
+instance StringSupersetConversion LT.Text T.Text where
+    convertStringUnsafe = LT.toStrict
+
+instance StringSupersetConversion BS.ByteString LBS.ByteString where
+    convertStringUnsafe = LBS.fromStrict
+
+instance StringSupersetConversion LBS.ByteString BS.ByteString where
+    convertStringUnsafe = LBS.toStrict
+
+---
+
+instance StringSupersetConversion T.Text [Unicode.Char] where
+    convertStringUnsafe = T.unpack
+
+instance StringSupersetConversion [Unicode.Char] T.Text where
+    convertStringUnsafe = T.pack
+
+instance StringSupersetConversion LT.Text [Unicode.Char] where
+    convertStringUnsafe = LT.unpack
+
+instance StringSupersetConversion [Unicode.Char] LT.Text where
+    convertStringUnsafe = LT.pack
+
+instance StringSupersetConversion BS.ByteString [Unicode.Char] where
+    convertStringUnsafe = BS.Char8.unpack
+
+instance StringSupersetConversion [Unicode.Char] BS.ByteString where
+    convertStringUnsafe = BS.Char8.pack
+
+instance StringSupersetConversion LBS.ByteString [Unicode.Char] where
+    convertStringUnsafe = LBS.Char8.unpack
+
+instance StringSupersetConversion [Unicode.Char] LBS.ByteString where
+    convertStringUnsafe = LBS.Char8.pack
diff --git a/library/ASCII/SupersetConversion.hs-boot b/library/ASCII/SupersetConversion.hs-boot
new file mode 100644
--- /dev/null
+++ b/library/ASCII/SupersetConversion.hs-boot
@@ -0,0 +1,6 @@
+module ASCII.SupersetConversion where
+
+import ASCII.Superset (StringSuperset)
+
+class (StringSuperset a, StringSuperset b) => StringSupersetConversion a b where
+    convertStringUnsafe :: a -> b
