require-callstack 0.1.0.0 → 0.2.0.0
raw patch · 5 files changed
+59/−85 lines, 5 filesdep +ghc-prim
Dependencies added: ghc-prim
Files
- CHANGELOG.md +5/−0
- require-callstack.cabal +6/−11
- src/RequireCallStack.hs +47/−9
- src/RequireCallStack/Internal.hs +0/−64
- test/Main.hs +1/−1
CHANGELOG.md view
@@ -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.
require-callstack.cabal view
@@ -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
src/RequireCallStack.hs view
@@ -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
− src/RequireCallStack/Internal.hs
@@ -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) ()
test/Main.hs view
@@ -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"