diff --git a/src/Text/Ascii.hs b/src/Text/Ascii.hs
--- a/src/Text/Ascii.hs
+++ b/src/Text/Ascii.hs
@@ -1,5 +1,8 @@
 {-# LANGUAGE UnicodeSyntax #-}
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
 -- | ASCII utility functions.
@@ -21,12 +24,17 @@
   , toLower
   , toUpper
   , isAlpha
-  , isDigit
-  , isNzDigit
   , isAlphaNum
-  , fromDigit
-  , fromNzDigit
-  , unsafeFromDigit
+  , isDecDigit
+  , isNzDecDigit
+  , fromDecDigit
+  , fromNzDecDigit
+  , unsafeFromDecDigit
+  , isBinDigit
+  , isNzBinDigit
+  , fromBinDigit
+  , fromNzBinDigit
+  , unsafeFromBinDigit
   , isOctDigit
   , isNzOctDigit
   , fromOctDigit
@@ -57,12 +65,17 @@
   , toLower8
   , toUpper8
   , isAlpha8
-  , isDigit8
-  , isNzDigit8
   , isAlphaNum8
-  , fromDigit8
-  , fromNzDigit8
-  , unsafeFromDigit8
+  , isDecDigit8
+  , isNzDecDigit8
+  , fromDecDigit8
+  , fromNzDecDigit8
+  , unsafeFromDecDigit8
+  , isBinDigit8
+  , isNzBinDigit8
+  , fromBinDigit8
+  , fromNzBinDigit8
+  , unsafeFromBinDigit8
   , isOctDigit8
   , isNzOctDigit8
   , fromOctDigit8
@@ -86,12 +99,17 @@
   ) where
 
 import Data.Checked
+import Data.Function (on)
 import Data.Char (ord, chr)
+import Data.String (IsString(..))
 import Data.Word (Word8)
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BL
 import qualified Data.Text as TS
 import qualified Data.Text.Lazy as TL
+import Data.Monoid (Monoid(..))
+import Data.CaseInsensitive (FoldCase(..))
+import Data.Hashable (Hashable(..))
 
 data IsAscii = IsAscii
 
@@ -129,6 +147,61 @@
 
 type Ascii α = Checked IsAscii α
 
+instance Eq α ⇒ Eq (Ascii α) where
+  (==) = (==) `on` checked
+  {-# INLINE (==) #-}
+
+instance Ord α ⇒ Ord (Ascii α) where
+  compare = compare `on` checked
+  {-# INLINE compare #-}
+
+instance Show α ⇒ Show (Ascii α) where
+  showsPrec p = showsPrec p . checked
+
+instance Monoid α ⇒ Monoid (Ascii α) where
+  mempty = trustMe mempty
+  {-# INLINE mempty #-}
+  mappend x y = trustMe $ mappend (checked x) (checked y)
+  {-# INLINE mappend #-}
+
+instance IsString α ⇒ IsString (Ascii α) where
+  fromString s | isAscii s = trustMe $ fromString s
+               | otherwise = error $ "Not an ASCII string: " ++ show s
+  {-# INLINE fromString #-}
+
+instance Hashable α ⇒ Hashable (Ascii α) where
+#if MIN_VERSION_hashable(1,2,0)
+  hashWithSalt s = hashWithSalt s . checked
+  {-# INLINE hashWithSalt #-}
+#else
+  hash = hash . checked
+  {-# INLINE hash #-}
+#endif
+
+instance FoldCase (Ascii Char) where
+  foldCase = trustMap toLower
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Ascii α) ⇒ FoldCase (Ascii [α]) where
+  foldCase = trustMap $ map $ checked . foldCase . trustThat IsAscii
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Ascii BS.ByteString) where
+  foldCase = trustMap $ BS.map toLower8
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Ascii BL.ByteString) where
+  foldCase = trustMap $ BL.map toLower8
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Ascii TS.Text) where
+  foldCase = trustMap $ TS.map toLower
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Ascii TL.Text) where
+  foldCase = trustMap $ TL.map toLower
+  {-# INLINE foldCase #-}
+
 -- | Map a character to its ASCII encoding if possible, otherwise
 --   return 'Nothing'.
 maybeAscii ∷ Char → Maybe Word8
@@ -136,7 +209,7 @@
              | otherwise = Nothing
 {-# INLINABLE maybeAscii #-}
 
--- | Encode an ASCII character. No checks is performed.
+-- | Encode an ASCII character. No checks are performed.
 ascii ∷ Char → Word8
 ascii = fromIntegral . ord
 {-# INLINE ascii #-}
@@ -193,40 +266,68 @@
 isAlpha c = isUpper c || isLower c
 {-# INLINABLE isAlpha #-}
 
--- | Test if a character is a decimal digit (/'0' ... '9'/).
-isDigit ∷ Char → Bool
-isDigit c = c >= '0' && c <= '9'
-{-# INLINE isDigit #-}
-
--- | Test if a character is a non-zero decimal digit (/'1' ... '9'/).
-isNzDigit ∷ Char → Bool
-isNzDigit c = c >= '1' && c <= '9'
-{-# INLINE isNzDigit #-}
-
 -- | Test if a character is either an ASCII letter or a decimal digit.
 isAlphaNum ∷ Char → Bool
-isAlphaNum c = isDigit c || isAlpha c
+isAlphaNum c = isDecDigit c || isAlpha c
 {-# INLINABLE isAlphaNum #-}
 
+-- | Test if a character is a decimal digit (/'0' ... '9'/).
+isDecDigit ∷ Char → Bool
+isDecDigit c = c >= '0' && c <= '9'
+{-# INLINE isDecDigit #-}
+
+-- | Test if a character is a non-zero decimal digit (/'1' ... '9'/).
+isNzDecDigit ∷ Char → Bool
+isNzDecDigit c = c >= '1' && c <= '9'
+{-# INLINE isNzDecDigit #-}
+
 -- | Map a decimal digit to the corresponding number. Return 'Nothing' on
 --   other inputs.
-fromDigit ∷ Num a ⇒ Char → Maybe a
-fromDigit c | isDigit c = Just $ unsafeFromDigit c
-            | otherwise = Nothing
-{-# INLINABLE fromDigit #-}
+fromDecDigit ∷ Num a ⇒ Char → Maybe a
+fromDecDigit c | isDecDigit c = Just $ unsafeFromDecDigit c
+               | otherwise    = Nothing
+{-# INLINABLE fromDecDigit #-}
 
 -- | Map non-zero decimal digits to the corresponding numbers. Return
 --   'Nothing' on other inputs.
-fromNzDigit ∷ Num a ⇒ Char → Maybe a
-fromNzDigit c | isNzDigit c = Just $ unsafeFromDigit c
-              | otherwise   = Nothing
-{-# INLINABLE fromNzDigit #-}
+fromNzDecDigit ∷ Num a ⇒ Char → Maybe a
+fromNzDecDigit c | isNzDecDigit c = Just $ unsafeFromDecDigit c
+                 | otherwise      = Nothing
+{-# INLINABLE fromNzDecDigit #-}
 
--- | Map decimal digits to the corresponding numbers. No checks is performed.
-unsafeFromDigit ∷ Num a ⇒ Char → a
-unsafeFromDigit c = fromIntegral (ord c - ord '0')
-{-# INLINE unsafeFromDigit #-}
+-- | Map decimal digits to the corresponding numbers. No checks are performed.
+unsafeFromDecDigit ∷ Num a ⇒ Char → a
+unsafeFromDecDigit c = fromIntegral (ord c - ord '0')
+{-# INLINE unsafeFromDecDigit #-}
 
+-- | Test if a character is a binary digit (/'0'/ or /'1'/).
+isBinDigit ∷ Char → Bool
+isBinDigit c = c == '0' || c == '1'
+{-# INLINE isBinDigit #-}
+
+-- | Test if a character is the non-zero binary digit (/'1'/).
+isNzBinDigit ∷ Char → Bool
+isNzBinDigit c = c == '1'
+{-# INLINE isNzBinDigit #-}
+
+-- | Map binary digits to the corresponding numbers. Return 'Nothing' on
+--   other inputs.
+fromBinDigit ∷ Num a ⇒ Char → Maybe a
+fromBinDigit c | isBinDigit c = Just $ unsafeFromBinDigit c
+               | otherwise    = Nothing
+{-# INLINABLE fromBinDigit #-}
+
+-- | Map the digit /'1'/ to the number /1/. Return 'Nothing' on other inputs.
+fromNzBinDigit ∷ Num a ⇒ Char → Maybe a
+fromNzBinDigit c | isNzBinDigit c = Just 1
+                 | otherwise      = Nothing
+{-# INLINABLE fromNzBinDigit #-}
+
+-- | Map binary digits to the corresponding numbers. No checks are performed.
+unsafeFromBinDigit ∷ Num a ⇒ Char → a
+unsafeFromBinDigit = unsafeFromDecDigit
+{-# INLINE unsafeFromBinDigit #-}
+
 -- | Test if a character is an octal digit (/'0' ... '7'/).
 isOctDigit ∷ Char → Bool
 isOctDigit c = c >= '0' && c <= '7'
@@ -251,9 +352,9 @@
                  | otherwise      = Nothing
 {-# INLINABLE fromNzOctDigit #-}
 
--- | Map octal digits to the corresponding numbers. No checks is performed.
+-- | Map octal digits to the corresponding numbers. No checks are performed.
 unsafeFromOctDigit ∷ Num a ⇒ Char → a
-unsafeFromOctDigit = unsafeFromDigit
+unsafeFromOctDigit = unsafeFromDecDigit
 {-# INLINE unsafeFromOctDigit #-}
 
 isLowAF ∷ Char → Bool
@@ -267,35 +368,35 @@
 -- | Test if a character is a lower-case hexadecimal digit
 --   (/'0' ... '9'/ or /'a' ... 'f'/).
 isLowHexDigit ∷ Char → Bool
-isLowHexDigit c = isDigit c || isLowAF c
+isLowHexDigit c = isDecDigit c || isLowAF c
 {-# INLINABLE isLowHexDigit #-}
 
 -- | Test if a character is a non-zero lower-case hexadecimal digit
 --   (/'1' ... '9'/ or /'a' ... 'f'/).
 isNzLowHexDigit ∷ Char → Bool
-isNzLowHexDigit c = isNzDigit c || isLowAF c
+isNzLowHexDigit c = isNzDecDigit c || isLowAF c
 {-# INLINABLE isNzLowHexDigit #-}
 
 -- | Map lower-case hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromLowHexDigit ∷ Num a ⇒ Char → Maybe a
-fromLowHexDigit c | isDigit c = Just $ unsafeFromDigit c
-                  | isLowAF c = Just $ fromLowAF c
-                  | otherwise = Nothing
+fromLowHexDigit c | isDecDigit c = Just $ unsafeFromDecDigit c
+                  | isLowAF c    = Just $ fromLowAF c
+                  | otherwise    = Nothing
 {-# INLINABLE fromLowHexDigit #-}
 
 -- | Map non-zero lower-case hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromNzLowHexDigit ∷ Num a ⇒ Char → Maybe a
-fromNzLowHexDigit c | isNzDigit c = Just $ unsafeFromDigit c
-                    | isLowAF c   = Just $ fromLowAF c
-                    | otherwise   = Nothing
+fromNzLowHexDigit c | isNzDecDigit c = Just $ unsafeFromDecDigit c
+                    | isLowAF c      = Just $ fromLowAF c
+                    | otherwise      = Nothing
 {-# INLINABLE fromNzLowHexDigit #-}
 
 -- | Map lower-case hexadecimal digits to the corresponding numbers.
---   No checks is performed.
+--   No checks are performed.
 unsafeFromLowHexDigit ∷ Num a ⇒ Char → a
-unsafeFromLowHexDigit c | c < 'a'   = unsafeFromDigit c
+unsafeFromLowHexDigit c | c < 'a'   = unsafeFromDecDigit c
                         | otherwise = fromLowAF c
 {-# INLINE unsafeFromLowHexDigit #-}
 
@@ -310,72 +411,72 @@
 -- | Test if a character is an upper-case hexadecimal digit
 --   (/'0' ... '9'/ or /'A' ... 'F'/).
 isUpHexDigit ∷ Char → Bool
-isUpHexDigit c = isDigit c || isUpAF c
+isUpHexDigit c = isDecDigit c || isUpAF c
 {-# INLINABLE isUpHexDigit #-}
 
 -- | Test if a character is a non-zero upper-case hexadecimal digit
 --   (/'1' ... '9'/ or /'A' ... 'F'/).
 isNzUpHexDigit ∷ Char → Bool
-isNzUpHexDigit c = isNzDigit c || isUpAF c
+isNzUpHexDigit c = isNzDecDigit c || isUpAF c
 {-# INLINABLE isNzUpHexDigit #-}
 
 -- | Map upper-case hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromUpHexDigit ∷ Num a ⇒ Char → Maybe a
-fromUpHexDigit c | isDigit c = Just $ unsafeFromDigit c
-                 | isUpAF c  = Just $ fromUpAF c
-                 | otherwise = Nothing
+fromUpHexDigit c | isDecDigit c = Just $ unsafeFromDecDigit c
+                 | isUpAF c     = Just $ fromUpAF c
+                 | otherwise    = Nothing
 {-# INLINABLE fromUpHexDigit #-}
 
 -- | Map non-zero upper-case hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromNzUpHexDigit ∷ Num a ⇒ Char → Maybe a
-fromNzUpHexDigit c | isNzDigit c = Just $ unsafeFromDigit c
-                   | isUpAF c    = Just $ fromUpAF c
-                   | otherwise   = Nothing
+fromNzUpHexDigit c | isNzDecDigit c = Just $ unsafeFromDecDigit c
+                   | isUpAF c       = Just $ fromUpAF c
+                   | otherwise      = Nothing
 {-# INLINABLE fromNzUpHexDigit #-}
 
 -- | Map upper-case hexadecimal digits to the corresponding numbers.
---   No checks is performed.
+--   No checks are performed.
 unsafeFromUpHexDigit ∷ Num a ⇒ Char → a
-unsafeFromUpHexDigit c | c < 'A'   = unsafeFromDigit c
+unsafeFromUpHexDigit c | c < 'A'   = unsafeFromDecDigit c
                        | otherwise = fromUpAF c
 {-# INLINE unsafeFromUpHexDigit #-}
 
 -- | Test if a character is a hexadecimal digit
 --   (/'0' ... '9'/ or /'a' ... 'f'/ or /'A' ... 'F'/).
 isHexDigit ∷ Char → Bool
-isHexDigit c = isDigit c || isUpAF c || isLowAF c
+isHexDigit c = isDecDigit c || isUpAF c || isLowAF c
 {-# INLINABLE isHexDigit #-}
 
 -- | Test if a character is a non-zero hexadecimal digit
 --   (/'1' ... '9'/ or /'a' ... 'f'/ or /'A' ... 'F'/).
 isNzHexDigit ∷ Char → Bool
-isNzHexDigit c = isNzDigit c || isUpAF c || isLowAF c
+isNzHexDigit c = isNzDecDigit c || isUpAF c || isLowAF c
 {-# INLINABLE isNzHexDigit #-}
 
 -- | Map hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromHexDigit ∷ Num a ⇒ Char → Maybe a
-fromHexDigit c | isDigit c = Just $ unsafeFromDigit c
-               | isUpAF c  = Just $ fromUpAF c
-               | isLowAF c = Just $ fromLowAF c
-               | otherwise = Nothing
+fromHexDigit c | isDecDigit c = Just $ unsafeFromDecDigit c
+               | isUpAF c     = Just $ fromUpAF c
+               | isLowAF c    = Just $ fromLowAF c
+               | otherwise    = Nothing
 {-# INLINABLE fromHexDigit #-}
 
 -- | Map non-zero hexadecimal digits to the corresponding numbers.
 --   Return 'Nothing' on other inputs.
 fromNzHexDigit ∷ Num a ⇒ Char → Maybe a
-fromNzHexDigit c | isNzDigit c = Just $ unsafeFromDigit c
-                 | isUpAF c    = Just $ fromUpAF c
-                 | isLowAF c   = Just $ fromLowAF c
-                 | otherwise   = Nothing
+fromNzHexDigit c | isNzDecDigit c = Just $ unsafeFromDecDigit c
+                 | isUpAF c       = Just $ fromUpAF c
+                 | isLowAF c      = Just $ fromLowAF c
+                 | otherwise      = Nothing
 {-# INLINABLE fromNzHexDigit #-}
 
--- | Map hexadecimal digits to the corresponding numbers. No checks is
+-- | Map hexadecimal digits to the corresponding numbers. No checks are
 --   performed.
 unsafeFromHexDigit ∷ Num a ⇒ Char → a
-unsafeFromHexDigit c | c < 'A'   = unsafeFromDigit c
+unsafeFromHexDigit c | c < 'A'   = unsafeFromDecDigit c
                      | c < 'a'   = fromUpAF c
                      | otherwise = fromLowAF c
 {-# INLINE unsafeFromHexDigit #-}
@@ -429,43 +530,73 @@
 isAlpha8 w = isUpper8 w || isLower8 w
 {-# INLINABLE isAlpha8 #-}
 
--- | Test if a byte is the encoding of a decimal digit (/'0' ... '9'/).
-isDigit8 ∷ Word8 → Bool
-isDigit8 w = w >= ascii '0' && w <= ascii '9'
-{-# INLINE isDigit8 #-}
-
--- | Test if a byte is the encoding of a non-zero decimal digit
---   (/'1' ... '9'/).
-isNzDigit8 ∷ Word8 → Bool
-isNzDigit8 w = w >= ascii '1' && w <= ascii '9'
-{-# INLINE isNzDigit8 #-}
-
 -- | Test if a byte is the encoding of either an ASCII letter
 --   or a decimal digit.
 isAlphaNum8 ∷ Word8 → Bool
-isAlphaNum8 w = isDigit8 w || isAlpha8 w
+isAlphaNum8 w = isDecDigit8 w || isAlpha8 w
 {-# INLINABLE isAlphaNum8 #-}
 
+-- | Test if a byte is the encoding of a decimal digit (/'0' ... '9'/).
+isDecDigit8 ∷ Word8 → Bool
+isDecDigit8 w = w >= ascii '0' && w <= ascii '9'
+{-# INLINE isDecDigit8 #-}
+
+-- | Test if a byte is the encoding of a non-zero decimal digit
+--   (/'1' ... '9'/).
+isNzDecDigit8 ∷ Word8 → Bool
+isNzDecDigit8 w = w >= ascii '1' && w <= ascii '9'
+{-# INLINE isNzDecDigit8 #-}
+
 -- | Map the encoding of a decimal digit to the corresponding number.
 --   Return 'Nothing' on other inputs.
-fromDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromDigit8 w | isDigit8 w = Just $ unsafeFromDigit8 w
-             | otherwise  = Nothing
-{-# INLINABLE fromDigit8 #-}
+fromDecDigit8 ∷ Num a ⇒ Word8 → Maybe a
+fromDecDigit8 w | isDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                | otherwise  = Nothing
+{-# INLINABLE fromDecDigit8 #-}
 
 -- | Map the encoding of a non-zero decimal digit to the corresponding number.
 --   Return 'Nothing' on other inputs.
-fromNzDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromNzDigit8 w | isNzDigit8 w = Just $ unsafeFromDigit8 w
-               | otherwise    = Nothing
-{-# INLINABLE fromNzDigit8 #-}
+fromNzDecDigit8 ∷ Num a ⇒ Word8 → Maybe a
+fromNzDecDigit8 w | isNzDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                  | otherwise       = Nothing
+{-# INLINABLE fromNzDecDigit8 #-}
 
 -- | Map the encoding of a decimal digit to the corresponding number.
---   No checks is performed.
-unsafeFromDigit8 ∷ Num a ⇒ Word8 → a
-unsafeFromDigit8 w = fromIntegral (w - ascii '0')
-{-# INLINE unsafeFromDigit8 #-}
+--   No checks are performed.
+unsafeFromDecDigit8 ∷ Num a ⇒ Word8 → a
+unsafeFromDecDigit8 w = fromIntegral (w - ascii '0')
+{-# INLINE unsafeFromDecDigit8 #-}
 
+-- | Test if a byte is the encoding of a binary digit (/'0'/ or /'1'/).
+isBinDigit8 ∷ Word8 → Bool
+isBinDigit8 w = w == ascii '0' || w == ascii '1'
+{-# INLINE isBinDigit8 #-}
+
+-- | Test if a byte is the encoding of the non-zero binary digit (/'1'/).
+isNzBinDigit8 ∷ Word8 → Bool
+isNzBinDigit8 w = w == ascii '1'
+{-# INLINE isNzBinDigit8 #-}
+
+-- | Map the encoding of a binary digit to the corresponding number.
+--   Return 'Nothing' on other inputs.
+fromBinDigit8 ∷ Num a ⇒ Word8 → Maybe a
+fromBinDigit8 w | isBinDigit8 w = Just $ unsafeFromBinDigit8 w
+                | otherwise     = Nothing
+{-# INLINABLE fromBinDigit8 #-}
+
+-- | Map the encoding of the digit /'1'/ to the number /1/.
+--   Return 'Nothing' on other inputs.
+fromNzBinDigit8 ∷ Num a ⇒ Word8 → Maybe a
+fromNzBinDigit8 w | isNzBinDigit8 w = Just 1
+                  | otherwise       = Nothing
+{-# INLINABLE fromNzBinDigit8 #-}
+
+-- | Map the encoding of a binary digit to the corresponding number.
+--   No checks are performed.
+unsafeFromBinDigit8 ∷ Num a ⇒ Word8 → a
+unsafeFromBinDigit8 = unsafeFromDecDigit8
+{-# INLINE unsafeFromBinDigit8 #-}
+
 -- | Test if a byte is the encoding of an octal digit (/'0' ... '7'/).
 isOctDigit8 ∷ Word8 → Bool
 isOctDigit8 w = w >= ascii '0' && w <= ascii '7'
@@ -492,9 +623,9 @@
 {-# INLINABLE fromNzOctDigit8 #-}
 
 -- | Map the encoding of an octal digit to the corresponding number.
---   No checks is performed.
+--   No checks are performed.
 unsafeFromOctDigit8 ∷ Num a ⇒ Word8 → a
-unsafeFromOctDigit8 = unsafeFromDigit8
+unsafeFromOctDigit8 = unsafeFromDecDigit8
 {-# INLINE unsafeFromOctDigit8 #-}
 
 isLowAF8 ∷ Word8 → Bool
@@ -508,35 +639,35 @@
 -- | Test if a byte is the encoding of a lower-case hexadecimal digit
 --   (/'0' ... '9'/ or /'a' ... 'f'/).
 isLowHexDigit8 ∷ Word8 → Bool
-isLowHexDigit8 w = isDigit8 w || isLowAF8 w
+isLowHexDigit8 w = isDecDigit8 w || isLowAF8 w
 {-# INLINABLE isLowHexDigit8 #-}
 
 -- | Test if a byte is the encoding of a non-zero lower-case hexadecimal digit
 --   (/'1' ... '9'/ or /'a' ... 'f'/).
 isNzLowHexDigit8 ∷ Word8 → Bool
-isNzLowHexDigit8 w = isNzDigit8 w || isLowAF8 w
+isNzLowHexDigit8 w = isNzDecDigit8 w || isLowAF8 w
 {-# INLINABLE isNzLowHexDigit8 #-}
 
 -- | Map the encoding of a lower-case hexadecimal digit to the corresponding
 --   number. Return 'Nothing' on other inputs.
 fromLowHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromLowHexDigit8 w | isDigit8 w = Just $ unsafeFromDigit8 w
-                   | isLowAF8 w = Just $ fromLowAF8 w
-                   | otherwise  = Nothing
+fromLowHexDigit8 w | isDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                   | isLowAF8 w    = Just $ fromLowAF8 w
+                   | otherwise     = Nothing
 {-# INLINABLE fromLowHexDigit8 #-}
 
 -- | Map the encoding of a non-zero lower-case hexadecimal digit to
 --   the corresponding number. Return 'Nothing' on other inputs.
 fromNzLowHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromNzLowHexDigit8 w | isNzDigit8 w = Just $ unsafeFromDigit8 w
-                     | isLowAF8 w   = Just $ fromLowAF8 w
-                     | otherwise    = Nothing
+fromNzLowHexDigit8 w | isNzDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                     | isLowAF8 w      = Just $ fromLowAF8 w
+                     | otherwise       = Nothing
 {-# INLINABLE fromNzLowHexDigit8 #-}
 
 -- | Map the encoding of a lower-case hexadecimal digit to the corresponding
---   number. No checks is performed.
+--   number. No checks are performed.
 unsafeFromLowHexDigit8 ∷ Num a ⇒ Word8 → a
-unsafeFromLowHexDigit8 w | w < ascii 'a' = unsafeFromDigit8 w
+unsafeFromLowHexDigit8 w | w < ascii 'a' = unsafeFromDecDigit8 w
                          | otherwise     = fromLowAF8 w
 {-# INLINE unsafeFromLowHexDigit8 #-}
 
@@ -551,72 +682,72 @@
 -- | Test if a byte is the encoding of an upper-case hexadecimal digit
 --   (/'0' ... '9'/ or /'A' ... 'F'/).
 isUpHexDigit8 ∷ Word8 → Bool
-isUpHexDigit8 w = isDigit8 w || isUpAF8 w
+isUpHexDigit8 w = isDecDigit8 w || isUpAF8 w
 {-# INLINABLE isUpHexDigit8 #-}
 
 -- | Test if a byte is the encoding of a non-zero upper-case hexadecimal digit
 --   (/'1' ... '9'/ or /'A' ... 'F'/).
 isNzUpHexDigit8 ∷ Word8 → Bool
-isNzUpHexDigit8 w = isNzDigit8 w || isUpAF8 w
+isNzUpHexDigit8 w = isNzDecDigit8 w || isUpAF8 w
 {-# INLINABLE isNzUpHexDigit8 #-}
 
 -- | Map the encoding of an upper-case hexadecimal digit to the corresponding
 --   number. Return 'Nothing' on other inputs.
 fromUpHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromUpHexDigit8 w | isDigit8 w = Just $ unsafeFromDigit8 w
-                  | isUpAF8 w  = Just $ fromUpAF8 w
-                  | otherwise  = Nothing
+fromUpHexDigit8 w | isDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                  | isUpAF8 w     = Just $ fromUpAF8 w
+                  | otherwise     = Nothing
 {-# INLINABLE fromUpHexDigit8 #-}
 
 -- | Map the encoding of a non-zero upper-case hexadecimal digit to
 --   the corresponding number. Return 'Nothing' on other inputs.
 fromNzUpHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromNzUpHexDigit8 w | isNzDigit8 w = Just $ unsafeFromDigit8 w
-                    | isUpAF8 w    = Just $ fromUpAF8 w
-                    | otherwise    = Nothing
+fromNzUpHexDigit8 w | isNzDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                    | isUpAF8 w       = Just $ fromUpAF8 w
+                    | otherwise       = Nothing
 {-# INLINABLE fromNzUpHexDigit8 #-}
 
 -- | Map the encoding of an upper-case hexadecimal digit to the corresponding
---   number. No checks is performed.
+--   number. No checks are performed.
 unsafeFromUpHexDigit8 ∷ Num a ⇒ Word8 → a
-unsafeFromUpHexDigit8 w | w < ascii 'A' = unsafeFromDigit8 w
+unsafeFromUpHexDigit8 w | w < ascii 'A' = unsafeFromDecDigit8 w
                         | otherwise     = fromUpAF8 w
 {-# INLINE unsafeFromUpHexDigit8 #-}
 
 -- | Test if a byte is the encoding of a hexadecimal digit
 --   (/'0' ... '9'/ or /'a' ... 'f'/ or /'A' ... 'F'/).
 isHexDigit8 ∷ Word8 → Bool
-isHexDigit8 w = isDigit8 w || isUpAF8 w || isLowAF8 w
+isHexDigit8 w = isDecDigit8 w || isUpAF8 w || isLowAF8 w
 {-# INLINABLE isHexDigit8 #-}
 
 -- | Test if a byte is the encoding of a non-zero hexadecimal digit
 --   (/'1' ... '9'/ or /'a' ... 'f'/ or /'A' ... 'F'/).
 isNzHexDigit8 ∷ Word8 → Bool
-isNzHexDigit8 w = isNzDigit8 w || isUpAF8 w || isLowAF8 w
+isNzHexDigit8 w = isNzDecDigit8 w || isUpAF8 w || isLowAF8 w
 {-# INLINABLE isNzHexDigit8 #-}
 
 -- | Map the encoding of a hexadecimal digit to the corresponding
 --   number. Return 'Nothing' on other inputs.
 fromHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromHexDigit8 w | isDigit8 w = Just $ unsafeFromDigit8 w
-                | isUpAF8 w  = Just $ fromUpAF8 w
-                | isLowAF8 w = Just $ fromLowAF8 w
-                | otherwise  = Nothing
+fromHexDigit8 w | isDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                | isUpAF8 w     = Just $ fromUpAF8 w
+                | isLowAF8 w    = Just $ fromLowAF8 w
+                | otherwise     = Nothing
 {-# INLINABLE fromHexDigit8 #-}
 
 -- | Map the encoding of a non-zero hexadecimal digit to the corresponding
 --   number. Return 'Nothing' on other inputs.
 fromNzHexDigit8 ∷ Num a ⇒ Word8 → Maybe a
-fromNzHexDigit8 w | isNzDigit8 w = Just $ unsafeFromDigit8 w
-                  | isUpAF8 w    = Just $ fromUpAF8 w
-                  | isLowAF8 w   = Just $ fromLowAF8 w
-                  | otherwise    = Nothing
+fromNzHexDigit8 w | isNzDecDigit8 w = Just $ unsafeFromDecDigit8 w
+                  | isUpAF8 w       = Just $ fromUpAF8 w
+                  | isLowAF8 w      = Just $ fromLowAF8 w
+                  | otherwise       = Nothing
 {-# INLINABLE fromNzHexDigit8 #-}
 
 -- | Map the encoding of a hexadecimal digit to the corresponding
---   number. No checks is performed.
+--   number. No checks are performed.
 unsafeFromHexDigit8 ∷ Num a ⇒ Word8 → a
-unsafeFromHexDigit8 w | w < ascii 'A' = unsafeFromDigit8 w
+unsafeFromHexDigit8 w | w < ascii 'A' = unsafeFromDecDigit8 w
                       | w < ascii 'a' = fromUpAF8 w
                       | otherwise     = fromLowAF8 w
 {-# INLINE unsafeFromHexDigit8 #-}
diff --git a/src/Text/Latin1.hs b/src/Text/Latin1.hs
--- a/src/Text/Latin1.hs
+++ b/src/Text/Latin1.hs
@@ -1,6 +1,9 @@
 {-# LANGUAGE UnicodeSyntax #-}
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE PatternGuards #-}
 {-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE TypeSynonymInstances #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 
 -- | Latin-1 utility functions.
@@ -36,12 +39,17 @@
   ) where
 
 import Data.Checked
+import Data.Function (on)
 import Data.Char (ord, chr)
+import Data.String (IsString(..))
 import Data.Word (Word8)
 import qualified Data.Text as TS
 import qualified Data.Text.Lazy as TL
 import Text.Ascii (Ascii)
 import qualified Text.Ascii as A
+import Data.Monoid (Monoid(..))
+import Data.CaseInsensitive (FoldCase(..))
+import Data.Hashable (Hashable(..))
 
 data IsLatin1 = IsLatin1
 
@@ -67,6 +75,53 @@
 
 type Latin1 α = Checked IsLatin1 α
 
+instance Eq α ⇒ Eq (Latin1 α) where
+  (==) = (==) `on` checked
+  {-# INLINE (==) #-}
+
+instance Ord α ⇒ Ord (Latin1 α) where
+  compare = compare `on` checked
+  {-# INLINE compare #-}
+
+instance Show α ⇒ Show (Latin1 α) where
+  showsPrec p = showsPrec p . checked
+
+instance Monoid α ⇒ Monoid (Latin1 α) where
+  mempty = trustMe mempty
+  {-# INLINE mempty #-}
+  mappend x y = trustMe $ mappend (checked x) (checked y)
+  {-# INLINE mappend #-}
+
+instance IsString α ⇒ IsString (Latin1 α) where
+  fromString s | isLatin1 s = trustMe $ fromString s
+               | otherwise  = error $ "Not a Latin-1 string: " ++ show s
+  {-# INLINE fromString #-}
+
+instance Hashable α ⇒ Hashable (Latin1 α) where
+#if MIN_VERSION_hashable(1,2,0)
+  hashWithSalt s = hashWithSalt s . checked
+  {-# INLINE hashWithSalt #-}
+#else
+  hash = hash . checked
+  {-# INLINE hash #-}
+#endif
+
+instance FoldCase (Latin1 Char) where
+  foldCase = trustMap toLower
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Latin1 α) ⇒ FoldCase (Latin1 [α]) where
+  foldCase = trustMap $ map $ checked . foldCase . trustThat IsLatin1
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Latin1 TS.Text) where
+  foldCase = trustMap $ TS.map toLower
+  {-# INLINE foldCase #-}
+
+instance FoldCase (Latin1 TL.Text) where
+  foldCase = trustMap $ TL.map toLower
+  {-# INLINE foldCase #-}
+
 -- | ASCII values are Latin-1 values.
 asciiIsLatin1 ∷ Ascii α → Latin1 α
 asciiIsLatin1 = trustMe . checked
@@ -79,7 +134,7 @@
               | otherwise  = Nothing
 {-# INLINABLE maybeLatin1 #-}
 
--- | Encode a Latin-1 character. No checks is performed.
+-- | Encode a Latin-1 character. No checks are performed.
 latin1 ∷ Char → Word8
 latin1 = fromIntegral . ord
 {-# INLINE latin1 #-}
@@ -138,7 +193,7 @@
 
 -- | Test if a character is either a Latin-1 letter or a decimal digit.
 isAlphaNum ∷ Char → Bool
-isAlphaNum c = A.isDigit c || isAlpha c
+isAlphaNum c = A.isDecDigit c || isAlpha c
 {-# INLINABLE isAlphaNum #-}
 
 -- | Test if a byte is the encoding of a Latin-1 control character.
@@ -188,6 +243,6 @@
 -- | Test if a byte is the encoding of either a Latin-1 letter
 --   or a decimal digit.
 isAlphaNum8 ∷ Word8 → Bool
-isAlphaNum8 w = A.isDigit8 w || isAlpha8 w
+isAlphaNum8 w = A.isDecDigit8 w || isAlpha8 w
 {-# INLINABLE isAlphaNum8 #-}
 
diff --git a/text-latin1.cabal b/text-latin1.cabal
--- a/text-latin1.cabal
+++ b/text-latin1.cabal
@@ -1,5 +1,5 @@
 Name: text-latin1
-Version: 0.1
+Version: 0.2
 Category: Text
 Stability: experimental
 Synopsis: Latin-1 (including ASCII) utility functions
@@ -25,9 +25,11 @@
 
 Library
   Build-Depends: base >= 4 && < 5
-               , data-checked
+               , data-checked >= 0.2
                , bytestring
                , text
+               , case-insensitive >= 1.0
+               , hashable >= 1.1
   Hs-Source-Dirs: src
   GHC-Options: -Wall
   Exposed-Modules:
