diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## 0.5.1 (2024-06-11)
+* add `Via` predicate
+
 ## 0.5.0 (2024-05-26)
 * add tentative operator versions of predicates
 * clear up WIP normalization story
diff --git a/rerefined.cabal b/rerefined.cabal
--- a/rerefined.cabal
+++ b/rerefined.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           rerefined
-version:        0.5.0
+version:        0.5.1
 synopsis:       Refinement types, again
 description:    Please see README.md.
 category:       Types, Data
@@ -50,6 +50,7 @@
       Rerefined.Predicate.Relational.Length
       Rerefined.Predicate.Relational.Value
       Rerefined.Predicate.Succeed
+      Rerefined.Predicate.Via
       Rerefined.Predicates
       Rerefined.Predicates.Operators
       Rerefined.Refine
@@ -74,8 +75,8 @@
       QuickCheck >=2.14 && <2.16
     , base >=4.18 && <5
     , mono-traversable >=1.0.17.0 && <1.1
-    , template-haskell >=2.19.0.0 && <2.22
+    , template-haskell >=2.19.0.0 && <2.23
     , text >=2.0 && <2.2
     , text-builder-linear >=0.1.2 && <0.2
-    , type-level-show >=0.2.1 && <0.3
+    , type-level-show >=0.2.1 && <0.4
   default-language: GHC2021
diff --git a/src/Rerefined/Predicate/Via.hs b/src/Rerefined/Predicate/Via.hs
new file mode 100644
--- /dev/null
+++ b/src/Rerefined/Predicate/Via.hs
@@ -0,0 +1,47 @@
+{-# LANGUAGE UndecidableInstances #-} -- for PredicateName
+{-# LANGUAGE OverloadedStrings #-} -- for builder
+{-# LANGUAGE AllowAmbiguousTypes #-} -- for validateVia
+
+module Rerefined.Predicate.Via where
+
+import Rerefined.Predicate.Common
+import TypeLevelShow.Utils
+import GHC.Exts ( Proxy# )
+
+{- | Predicate @p@ is implemented via predicate @pVia@.
+
+This is useful when you need a unique data type for a predicate (e.g. for
+instances, clarity), but the predicate it tests already exists. In such cases,
+it would be nice to reuse the existing predicate, while printing both predicates
+in failures.
+
+'Via' assists in doing this. Implement your 'Predicate' instance as normal, then
+implement 'Refine' with:
+
+    'validate' = 'validateVia' \@pVia
+
+Now when this predicate fails, it will print the @pVia@ failure with a wrapper
+stating the name of the original predicate.
+
+Note that you may not use @DerivingVia@ because it only works on the last
+parameter of a multi-parameter type class, and I don't want to switch the order
+of the 'Refine' parameters. Even if I did, @DerivingVia@ isn't much different to
+or easier than this.
+-}
+data Via pVia p
+instance (Predicate p, Predicate pVia)
+  => Predicate (Via pVia p) where
+    type PredicateName d (Via pVia p) = ShowParen (d > 9)
+        ("Via " ++ PredicateName 10 p ++ " " ++ PredicateName 10 pVia)
+instance (Refine pVia a, Predicate p, KnownPredicateName p)
+  => Refine (Via pVia p) a where
+    validate _p a =
+        case validate (proxy# @pVia) a of
+          Nothing -> Nothing
+          Just e  -> validateFail (proxy# @p) "via" [e]
+
+validateVia
+    :: forall pVia p a
+    .  (Refine pVia a, Predicate p, KnownPredicateName p)
+    => Proxy# p -> a -> Maybe RefineFailure
+validateVia _p = validate (proxy# @(Via pVia p))
diff --git a/src/Rerefined/Predicates.hs b/src/Rerefined/Predicates.hs
--- a/src/Rerefined/Predicates.hs
+++ b/src/Rerefined/Predicates.hs
@@ -5,6 +5,7 @@
   -- * Base
     Succeed
   , Fail
+  , Via, validateVia
 
   -- * Logical
   , And, Iff, If, Nand, Nor, Not, Or, Xor
@@ -18,5 +19,6 @@
 
 import Rerefined.Predicate.Succeed
 import Rerefined.Predicate.Fail
+import Rerefined.Predicate.Via
 import Rerefined.Predicate.Logical
 import Rerefined.Predicate.Relational
