diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
-# 0.1.0.0
+# 0.2.0.1 (2023-03-12)
+
+* Fix build on GHC 9.4 and 9.6 by adding `UndecidableInstances`.
+
+# 0.2.0 (2021-10-11)
+
+* Add a type role for `Fin`, which was formerly `Coercible` in unsafe ways.
+* Fix the `Read` instance erroring on out-of-range values; it now merely fails.
+* Add `Attenuable` instances for `attenuation-0.2.0`.
+
+# 0.1.0.0 (2021-09-07)
 
 Initial version.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# fin-int
+
+Implements size-indexed bounded natural number types as a newtype over Int.
diff --git a/fin-int.cabal b/fin-int.cabal
--- a/fin-int.cabal
+++ b/fin-int.cabal
@@ -1,50 +1,47 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.33.0.
+-- This file has been generated from package.yaml by hpack version 0.35.1.
 --
 -- see: https://github.com/sol/hpack
---
--- hash: 7c84a39433ed8a33ecfb2a72df2187a958991c0069c5db23021bc938a85c54d2
 
 name:           fin-int
-version:        0.1.0.0
-synopsis:       The type of finite sets with elements identified by the ordinals.
-description:    This provides a newtype Fin containing an Int with an invariant that its
-                value is less than its type-level Nat bound.  It aims to have low overhead
-                compared to Ints, and is suitable for use as the index type of
+version:        0.2.0.1
+synopsis:       Finite sets of static size
+description:    This provides a newtype @Fin@ containing an @Int@ with an invariant that
+                its value is less than its type-level @Nat@ bound.  It aims to have low
+                overhead compared to @Int@s, and is suitable for use as the index type of
                 length-indexed vectors.
 category:       Data
-homepage:       https://github.com/google/hs-fin-vec#readme
-bug-reports:    https://github.com/google/hs-fin-vec/issues
+homepage:       https://github.com/awpr/fin-vec#readme
+bug-reports:    https://github.com/awpr/fin-vec/issues
 author:         Lennart Augustsson <lennart@augustsson.net>
-maintainer:     Andrew Pritchard <awpr@google.com>
-copyright:      2017-2021 Google LLC
+maintainer:     Andrew Pritchard <awpritchard@gmail.com>
+copyright:      2017-2021 Google LLC; 2022 Andrew Pritchard
 license:        Apache-2.0
 license-file:   LICENSE
 build-type:     Simple
 extra-source-files:
     CHANGELOG.md
+    README.md
 
 source-repository head
   type: git
-  location: https://github.com/google/hs-fin-vec
+  location: https://github.com/awpr/fin-vec
   subdir: fin-int
 
 library
   exposed-modules:
       Data.Fin.Int
       Data.Fin.Int.Explicit
-  other-modules:
-      Paths_fin_int
   hs-source-dirs:
       src
   build-depends:
       QuickCheck >=2.5 && <2.15
-    , attenuation >=0.1 && <0.2
-    , base >=4.12 && <4.16
+    , attenuation >=0.1 && <0.3
+    , base >=4.12 && <4.19
     , data-default-class >=0.0 && <0.2
     , deepseq >=1.1 && <1.5
-    , portray >=0.1 && <0.2
-    , portray-diff >=0.1 && <0.2
-    , sint >=0.1 && <0.2
+    , portray >=0.1 && <0.4
+    , portray-diff ==0.1.*
+    , sint >=0.1 && <0.3
   default-language: Haskell2010
diff --git a/src/Data/Fin/Int.hs b/src/Data/Fin/Int.hs
--- a/src/Data/Fin/Int.hs
+++ b/src/Data/Fin/Int.hs
@@ -95,7 +95,7 @@
 fin :: forall n. (HasCallStack, KnownNat n) => Int -> Fin n
 fin = E.fin sintVal
 
--- | This is similar to 'fromInteger', but you get a stack trace on error.
+-- | Generalized 'fin' that works on any 'Integral' type.
 {-# INLINE finFromIntegral #-}
 finFromIntegral
   :: forall n a
@@ -103,8 +103,7 @@
   => a -> Fin n
 finFromIntegral = E.finFromIntegral sintVal
 
--- | Like 'fin', but doesn't do any bounds checks. However, unlike
--- 'unsafeFin', this is safe (by virtue of the type constraints).
+-- | Construct a 'Fin' from a 'GHC.TypeNats.Nat' known to be in-bounds.
 knownFin :: forall i n. (KnownNat i, i <= n - 1) => Fin n
 knownFin = E.knownFin (sintVal @i)
 {-# INLINE knownFin #-}
@@ -272,6 +271,8 @@
 shiftFin = E.shiftFin sintVal
 
 -- | 'unshiftFin' decreases the value and bound of a Fin both by @m@.
+--
+-- Raises an exception if the result would be negative.
 unshiftFin
   :: forall m n
    . (HasCallStack, KnownNat m, KnownNat n)
@@ -294,13 +295,17 @@
 concatFin :: forall m n. KnownNat m => Either (Fin m) (Fin n) -> Fin (m + n)
 concatFin = E.concatFin sintVal
 
--- | Convert to a possibly smaller type.
--- This function fails if the number is too big.
+-- | Convert to a 'Fin' with a (potentially) smaller bound.
+--
+-- This function throws an exception if the number is out of range of the
+-- target type.
 {-# INLINE unembed #-}
 unembed :: (HasCallStack, KnownNat n) => Fin m -> Fin n
 unembed = E.unembed sintVal
 
--- | Convert to a possibly smaller type or return Nothing if out of bounds.
+-- | Convert to a 'Fin' with a (potentially) smaller bound.
+--
+-- Returns 'Nothing' if the number is out of range of the target type.
 {-# INLINE tryUnembed #-}
 tryUnembed :: KnownNat n => Fin m -> Maybe (Fin n)
 tryUnembed = E.tryUnembed sintVal
diff --git a/src/Data/Fin/Int/Explicit.hs b/src/Data/Fin/Int/Explicit.hs
--- a/src/Data/Fin/Int/Explicit.hs
+++ b/src/Data/Fin/Int/Explicit.hs
@@ -1,4 +1,5 @@
 -- Copyright 2017-2021 Google LLC
+-- Copyright 2022 Andrew Pritchard
 --
 -- Licensed under the Apache License, Version 2.0 (the "License");
 -- you may not use this file except in compliance with the License.
@@ -21,11 +22,15 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE MagicHash #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE NoStarIsType #-}
+{-# LANGUAGE RoleAnnotations #-}
 {-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE UndecidableInstances #-}
 {-# LANGUAGE ViewPatterns #-}
 
 -- | Finite natural numbers, with upper bound as part of the type.
@@ -71,10 +76,10 @@
          , unsafeFin, unsafePred, unsafeSucc, unsafeCoFin, unsafeCoInt
          ) where
 
-import Control.Arrow (first)
 import Control.DeepSeq (NFData(rnf))
 import Data.Coerce (coerce)
 import Data.Data (Data)
+import Data.Maybe (mapMaybe)
 import Data.Type.Coercion (Coercion(..))
 import GHC.Stack (HasCallStack, withFrozenCallStack)
 import GHC.TypeNats
@@ -85,7 +90,12 @@
 import Data.Default.Class (Default(..))
 import Data.Portray (Portray)
 import Data.Portray.Diff (Diff)
-import Data.Type.Attenuation (Attenuation, coercible)
+import Data.Type.Attenuation
+         ( Attenuation, coercible
+#if MIN_VERSION_attenuation(0, 2, 0)
+         , Attenuable(..)
+#endif
+         )
 import Test.QuickCheck (Arbitrary(..), arbitraryBoundedEnum)
 
 import Data.SInt (SInt, unSInt, sintVal)
@@ -102,26 +112,39 @@
   -- Fin Read/Show behave like other numeric newtypes: drop the \"Fin\".
   deriving newtype (Show, Portray, Diff)
 
+-- Prevent 'Coercible' across 'Fin's of different bounds.
+--
+-- For the safe direction, we have 'Attenuable' instead.
+type role Fin nominal
+
 -- | Constraint synonym for naturals @n@ s.t. @'Fin' n@ is inhabited.
 type FinSize n = (KnownNat n, 1 <= n)
 
 instance KnownNat n => Read (Fin n) where
-  readsPrec p s = first (finFromIntegral sintVal) <$> readsPrec @Integer p s
+  readsPrec p s =
+    mapMaybe (\ (x, s') -> (,s') <$> tryFin sintVal x) $
+    readsPrec @Integer p s
 
 instance FinSize n => Arbitrary (Fin n) where
   arbitrary = arbitraryBoundedEnum
 
 instance NFData (Fin n) where rnf (Fin x) = rnf x
 
+#if MIN_VERSION_attenuation(0,2,0)
+instance Attenuable (Fin n) Int
+instance m <= n => Attenuable (Fin m) (Fin n)
+#endif
+
 -- | Construct a 'Fin' from an 'Int', with bounds checks.
+{-# INLINE fin #-}
 fin :: HasCallStack => SInt n -> Int -> Fin n
 fin (unSInt -> !n) i
   | i <  0 = error $ "Fin: number out of range " ++ show i ++ " < 0"
   | i >= n = error $ "Fin: number out of range " ++ show i ++ " >= " ++ show n
   | otherwise = Fin i
 
--- | This is similar to 'fromInteger', but you get a stack trace on error.
-{-# INLINE fin #-}
+-- | Generalized 'fin' that works on any 'Integral' type.
+{-# INLINE finFromIntegral #-}
 finFromIntegral
   :: (HasCallStack, Integral a, Show a)
   => SInt n -> a -> Fin n
@@ -191,7 +214,7 @@
 -- raising the error inside of x, so an invalid Fin can never be returned.
 complementFin sn x = unsafeFin (unSInt sn - 1 - finToInt x)
 
--- | This is like 'fromIntegral', but without the annoying context.
+-- | Convert a 'Fin' to the underlying 'Int' representing it.
 finToInt :: Fin n -> Int
 finToInt (Fin i) = i
 {-# INLINE finToInt #-}
@@ -460,6 +483,8 @@
 {-# INLINEABLE chkMul #-}
 
 -- | Restricted coercion to larger Fin types.
+--
+-- This is also available as an 'Attenuable' instance.
 attLT :: (n <= m) => Attenuation (Fin n) (Fin m)
 attLT = coercible
 
@@ -472,6 +497,8 @@
 attMinus = coercible
 
 -- | Restricted coercion to Int.
+--
+-- This is also available as an 'Attenuable' instance.
 attInt :: Attenuation (Fin n) Int
 attInt = coercible
 
@@ -500,6 +527,8 @@
 tryUnshiftFin sm sn (Fin x) = tryFin sn (x - unSInt sm)
 
 -- | 'unshiftFin' decreases the value and bound of a Fin both by @m@.
+--
+-- Raises an exception if the result would be negative.
 unshiftFin :: HasCallStack => SInt m -> SInt n -> Fin (m+n) -> Fin n
 unshiftFin sm sn (Fin x) = fin sn (x - unSInt sm)
 
@@ -516,18 +545,24 @@
   Left (Fin x) -> Fin x
   Right x -> shiftFin sm x
 
--- | Convert to a bigger type.
+-- | Convert to a 'Fin' with a larger bound.
+--
+-- This is equivalent to a specialization of 'Data.Type.Attenuation.attenuate'.
 {-# INLINE embed #-}
 embed :: (m <= n) => Fin m -> Fin n
 embed (Fin i) = Fin i
 
--- | Convert to a possibly smaller type.
--- This function fails if the number is too big.
+-- | Convert to a 'Fin' with a (potentially) smaller bound.
+--
+-- This function throws an exception if the number is out of range of the
+-- target type.
 {-# INLINE unembed #-}
 unembed :: HasCallStack => SInt n -> Fin m -> Fin n
 unembed sn (Fin i) = ufin sn i
 
--- | Convert to a possibly smaller type or return Nothing if out of bounds.
+-- | Convert to a 'Fin' with a (potentially) smaller bound.
+--
+-- Returns 'Nothing' if the number is out of range of the target type.
 tryUnembed :: SInt n -> Fin m -> Maybe (Fin n)
 tryUnembed sn (Fin i) = tryFin sn i
 
