diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for require-callstack
 
+## 0.2.0.0
+
+* [#2](https://github.com/parsonsmatt/require-callstack/pull/2) 
+    * A simpler and safer implementation
+
 ## 0.1.0.0 
 
 * First version. Released on an unsuspecting world.
diff --git a/require-callstack.cabal b/require-callstack.cabal
--- a/require-callstack.cabal
+++ b/require-callstack.cabal
@@ -1,16 +1,15 @@
 cabal-version:      3.0
 name:               require-callstack
-version:            0.1.0.0
+version:            0.2.0.0
 synopsis:           Propagate HasCallStack with constraints
-description:        See the README for more information.
+description:        See the README for more information about this package.
 license:            MIT
 license-file:       LICENSE
 author:             parsonsmatt
 maintainer:         parsonsmatt@gmail.com
--- copyright:
 category:           Development
 build-type:         Simple
-extra-doc-files:    
+extra-doc-files:
     CHANGELOG.md
     README.md
 bug-reports:     https://github.com/parsonsmatt/require-callstack/issues
@@ -25,21 +24,17 @@
 
 library
     import:           warnings
-    exposed-modules:  
+    exposed-modules:
         RequireCallStack
-        RequireCallStack.Internal
-    -- other-modules:
-    -- other-extensions:
-    build-depends:    
+    build-depends:
         base >= 4.12 && < 5
+      , ghc-prim
     hs-source-dirs:   src
     default-language: Haskell2010
 
 test-suite require-callstack-test
     import:           warnings
     default-language: Haskell2010
-    -- other-modules:
-    -- other-extensions:
     type:             exitcode-stdio-1.0
     hs-source-dirs:   test
     main-is:          Main.hs
diff --git a/src/RequireCallStack.hs b/src/RequireCallStack.hs
--- a/src/RequireCallStack.hs
+++ b/src/RequireCallStack.hs
@@ -1,5 +1,7 @@
-{-# language RankNTypes, FlexibleContexts, FlexibleInstances, ImpredicativeTypes, MultiParamTypeClasses, DataKinds, ConstraintKinds #-}
+{-# language RankNTypes, MultiParamTypeClasses, DataKinds, ConstraintKinds, ImplicitParams, UndecidableInstances #-}
 
+{-# OPTIONS_GHC -Wno-orphans -Wno-missing-methods #-}
+
 -- | This module provides utilities to ensure that you propagate
 -- 'HasCallStack' constraints by introducing a class 'RequireCallStack'
 -- which can only be discharged using the 'provideCallStack' function.
@@ -22,11 +24,11 @@
 -- easier, you can use 'provideCallStack' to dismiss the constraint.
 --
 -- @
--- foo :: RequireCallStack => Int -> String
--- foo = error "oh no"
+-- foo :: `RequireCallStack` => `Int` -> `String`
+-- foo = `error` "oh no"
 --
--- bar :: Int -> String
--- bar i = provideCallStack $ foo i
+-- bar :: `Int` -> `String`
+-- bar i = `provideCallStack` `$` foo i
 -- @
 --
 -- Couple this with @annotated-exception@ library for excellent provenance
@@ -34,12 +36,14 @@
 module RequireCallStack
     ( RequireCallStack
     , RequireCallStackImpl
+    , ProvideCallStack
     , provideCallStack
     , errorRequireCallStack
     ) where
 
-import GHC.Stack
-import RequireCallStack.Internal
+import GHC.Stack (HasCallStack)
+import GHC.Classes (IP(..))
+import GHC.TypeLits (TypeError, ErrorMessage(..))
 
 -- | This constraint is similar to 'HasCallStack' in that it's presence
 -- will capture a stack frame for the call site of the function. This helps
@@ -48,9 +52,43 @@
 -- @since 0.1.0.0
 type RequireCallStack = (HasCallStack, RequireCallStackImpl)
 
--- | Raise an 'ErrorCall' and incur a 'RequireCallStack' constraint while
--- you do so. This
+-- | If you're running into this class, then you need to add
+-- 'RequireCallStack' to your function's signature, or discharge the
+-- constraint using 'provideCallStack'.
 --
 -- @since 0.1.0.0
+type RequireCallStackImpl = ?provideCallStack :: ProvideCallStack
+
+-- | The constructor for this type is intentionally not exported
+data ProvideCallStack = ProvideCallStack
+
+-- | Raise an 'Control.Exception.ErrorCall' and incur a 'RequireCallStack'
+-- constraint while you do so. This
+--
+-- @since 0.1.0.0
 errorRequireCallStack :: RequireCallStack => String -> x
 errorRequireCallStack = error
+
+instance TypeError ('Text "Add RequireCallStack to your function context or use provideCallStack") => IP "provideCallStack" ProvideCallStack
+
+-- | Satisfy a 'RequireCallStack' constraint for the given block. Can be
+-- used instead of propagating a 'RequireCallStack' up the call graph.
+--
+-- Usage:
+--
+-- @
+-- main :: `IO` ()
+-- main = do
+--   `provideCallStack` `$` do
+--       `errorRequireCallStack` "hello"
+-- @
+--
+-- Note how @main@ does not have a 'HasCallStack' or 'RequireCallStack'
+-- constraint. This function eliminates them, so that
+-- 'errorRequireCallStack' can be called without compilation error.
+--
+-- @since 0.1.0.0
+provideCallStack :: (RequireCallStackImpl => r) -> r
+provideCallStack r = r
+  where
+    ?provideCallStack = ProvideCallStack
diff --git a/src/RequireCallStack/Internal.hs b/src/RequireCallStack/Internal.hs
deleted file mode 100644
--- a/src/RequireCallStack/Internal.hs
+++ /dev/null
@@ -1,64 +0,0 @@
-{-# language RankNTypes, FlexibleContexts, FlexibleInstances, ImpredicativeTypes, MultiParamTypeClasses, DataKinds, ConstraintKinds #-}
-
--- | The implementation details in this module are subject to change
--- and breaking without a corresponding PVP version bump. Import at your
--- own risk.
-module RequireCallStack.Internal where
-
-import Unsafe.Coerce
-import GHC.Stack
-
--- | If you're running into this class, then you need to add
--- 'RequireCallStack' to your function's signature, or discharge the
--- constraint using 'provideCallStack'.
---
--- I'd like to provide a 'TypeError' instance here with a good message, but
--- unfortunately, I won't be able to do that because it fails early. I need
--- @Unsatisfiable@ in GHC 9.8 for that. So, until I get that, you'll  have
--- to see an error message that smuggles the suggestion in:
---
--- > No instance for 'Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack'
---
--- Which, hey, it is better than nothing!
---
--- @since 0.1.0.0
-class Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack
-
--- | An alias to make referring to
--- 'Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack' easier,
--- since it is a bit of a mouthful.
---
--- If you see this, you probably need to either add 'RequireCallStack' to
--- the function constraints, or you need to call 'provideCallStack' to
--- discharge it.
---
--- @since 0.1.0.0
-type RequireCallStackImpl = Add_RequireCallStack_ToFunctionContext_OrUse_provideCallStack
-
--- | An internal detail. This is a specialization of the trick used in the
--- @reflection@ library to reify constraints. It's based on some GHC
--- trickery - notably, that dictionaries become runtime parameters, and
--- a no method dictionary has the same runtime rep as @()@.
---
--- @since 0.1.0.0
-newtype MagicCallStack r = MagicCallStack (RequireCallStackImpl => r)
-
--- | Satisfy a 'RequireCallStack' constraint for the given block. Can be
--- used instead of propagating a 'RequireCallStack' up the call graph.
---
--- Usage:
---
--- @
--- main :: IO ()
--- main = do
---   provideCallStack $ do
---       errorRequireCallStack "hello"
--- @
---
--- Note how 'main' does not have a 'HasCallStack' or 'RequireCallStack'
--- constraint. This function eliminates them, so that
--- 'errorRequireCallStack' can be called without compilation error.
---
--- @since 0.1.0.0
-provideCallStack :: HasCallStack => (RequireCallStackImpl => r) -> r
-provideCallStack r = (unsafeCoerce (MagicCallStack r) :: () -> r) ()
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -20,7 +20,7 @@
 main :: IO ()
 main = do
     -- won't work, no callstack
-   -- panic "asdf"
+    -- panic "asdf"
 
     provideCallStack $ do
             -- panic "one level of provide callstack"
