How to throw an exception with CallStack?












3















I have Exception type UnknownException, which I'd like to include the CallStack when it's thrown.



module Main where

import Control.Exception (Exception, throw)

newtype UnknownException = UnknownException
{ caller :: String
} deriving (Show)

instance Exception UnknownException

main :: IO ()
main = willThrow

willThrow :: IO ()
willThrow = throw $ UnknownException "willThrow"


I'd like the above example to print logs like this



example-exe: UnknownException {caller = "willThrow"}
CallStack (from HasCallStack):
willThrow, called at app/Main.hs:16:13 in main:Main
main, called at app/Main.hs:13:8 in main:Main


but actually printed:



example-exe: UnknownException {caller = "willThrow"}


Also, is it a good practice to include CallStack in exceptions in Haskell?










share|improve this question



























    3















    I have Exception type UnknownException, which I'd like to include the CallStack when it's thrown.



    module Main where

    import Control.Exception (Exception, throw)

    newtype UnknownException = UnknownException
    { caller :: String
    } deriving (Show)

    instance Exception UnknownException

    main :: IO ()
    main = willThrow

    willThrow :: IO ()
    willThrow = throw $ UnknownException "willThrow"


    I'd like the above example to print logs like this



    example-exe: UnknownException {caller = "willThrow"}
    CallStack (from HasCallStack):
    willThrow, called at app/Main.hs:16:13 in main:Main
    main, called at app/Main.hs:13:8 in main:Main


    but actually printed:



    example-exe: UnknownException {caller = "willThrow"}


    Also, is it a good practice to include CallStack in exceptions in Haskell?










    share|improve this question

























      3












      3








      3








      I have Exception type UnknownException, which I'd like to include the CallStack when it's thrown.



      module Main where

      import Control.Exception (Exception, throw)

      newtype UnknownException = UnknownException
      { caller :: String
      } deriving (Show)

      instance Exception UnknownException

      main :: IO ()
      main = willThrow

      willThrow :: IO ()
      willThrow = throw $ UnknownException "willThrow"


      I'd like the above example to print logs like this



      example-exe: UnknownException {caller = "willThrow"}
      CallStack (from HasCallStack):
      willThrow, called at app/Main.hs:16:13 in main:Main
      main, called at app/Main.hs:13:8 in main:Main


      but actually printed:



      example-exe: UnknownException {caller = "willThrow"}


      Also, is it a good practice to include CallStack in exceptions in Haskell?










      share|improve this question














      I have Exception type UnknownException, which I'd like to include the CallStack when it's thrown.



      module Main where

      import Control.Exception (Exception, throw)

      newtype UnknownException = UnknownException
      { caller :: String
      } deriving (Show)

      instance Exception UnknownException

      main :: IO ()
      main = willThrow

      willThrow :: IO ()
      willThrow = throw $ UnknownException "willThrow"


      I'd like the above example to print logs like this



      example-exe: UnknownException {caller = "willThrow"}
      CallStack (from HasCallStack):
      willThrow, called at app/Main.hs:16:13 in main:Main
      main, called at app/Main.hs:13:8 in main:Main


      but actually printed:



      example-exe: UnknownException {caller = "willThrow"}


      Also, is it a good practice to include CallStack in exceptions in Haskell?







      haskell exception






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 2 at 0:00









      Leo ZhangLeo Zhang

      1,25411323




      1,25411323
























          1 Answer
          1






          active

          oldest

          votes


















          4














          Yeah I think it is good practice. If you are compiling with profiling, you can simply use



          currentCallStack :: IO [String]


          from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.



          But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So



          main :: IO ()
          main = print f

          f :: Int
          f = g

          g :: HasCallStack => Int
          g = h

          h :: HasCallStack => Int
          h = error (show callStack)


          Will report the call stack up to g, but will omit f. Sadly,




          If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.




          That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome.
          It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.






          share|improve this answer
























          • There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

            – Leo Zhang
            Jan 2 at 15:48











          • Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

            – Leo Zhang
            Jan 2 at 15:53











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999891%2fhow-to-throw-an-exception-with-callstack%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          4














          Yeah I think it is good practice. If you are compiling with profiling, you can simply use



          currentCallStack :: IO [String]


          from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.



          But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So



          main :: IO ()
          main = print f

          f :: Int
          f = g

          g :: HasCallStack => Int
          g = h

          h :: HasCallStack => Int
          h = error (show callStack)


          Will report the call stack up to g, but will omit f. Sadly,




          If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.




          That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome.
          It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.






          share|improve this answer
























          • There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

            – Leo Zhang
            Jan 2 at 15:48











          • Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

            – Leo Zhang
            Jan 2 at 15:53
















          4














          Yeah I think it is good practice. If you are compiling with profiling, you can simply use



          currentCallStack :: IO [String]


          from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.



          But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So



          main :: IO ()
          main = print f

          f :: Int
          f = g

          g :: HasCallStack => Int
          g = h

          h :: HasCallStack => Int
          h = error (show callStack)


          Will report the call stack up to g, but will omit f. Sadly,




          If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.




          That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome.
          It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.






          share|improve this answer
























          • There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

            – Leo Zhang
            Jan 2 at 15:48











          • Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

            – Leo Zhang
            Jan 2 at 15:53














          4












          4








          4







          Yeah I think it is good practice. If you are compiling with profiling, you can simply use



          currentCallStack :: IO [String]


          from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.



          But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So



          main :: IO ()
          main = print f

          f :: Int
          f = g

          g :: HasCallStack => Int
          g = h

          h :: HasCallStack => Int
          h = error (show callStack)


          Will report the call stack up to g, but will omit f. Sadly,




          If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.




          That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome.
          It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.






          share|improve this answer













          Yeah I think it is good practice. If you are compiling with profiling, you can simply use



          currentCallStack :: IO [String]


          from GHC.Stack. Notice that it's in IO but I would consider it fine to unsafePerformIO this when you throw the error if you are in pure code. Because all bottoms are equal denotationally, there's not really any violation of purity.



          But if you want to get at call stacks without profiling (say you want to include it in a log message on production), you have to do more. You have to include HasCallStack constraints everywhere you would like the stack to be reported. So



          main :: IO ()
          main = print f

          f :: Int
          f = g

          g :: HasCallStack => Int
          g = h

          h :: HasCallStack => Int
          h = error (show callStack)


          Will report the call stack up to g, but will omit f. Sadly,




          If there is no CallStack in scope and the enclosing definition has an explicit type signature, GHC will solve the HasCallStack constraint for the singleton CallStack containing just the current call-site.




          That means it will omits any callers of f, even if they do have HasCallStack just because f doesn't have such a constraint. I find this terribly cumbersome.
          It's a fairly recent feature, so I hope the GHC team has something better in mind that they using this to move toward.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 2 at 0:38









          luquiluqui

          48.2k6114168




          48.2k6114168













          • There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

            – Leo Zhang
            Jan 2 at 15:48











          • Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

            – Leo Zhang
            Jan 2 at 15:53



















          • There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

            – Leo Zhang
            Jan 2 at 15:48











          • Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

            – Leo Zhang
            Jan 2 at 15:53

















          There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

          – Leo Zhang
          Jan 2 at 15:48





          There is no other way to get a complete callstack without adding HasCallStack everywhere? Yeah, having to add that everywhere is very cumbersome. A complete callstack would be very useful.

          – Leo Zhang
          Jan 2 at 15:48













          Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

          – Leo Zhang
          Jan 2 at 15:53





          Also, I found if you remove the HasCallStack from g, and let f to have HasCallStack, then the callstack will not include f even if f has HasCallStack.

          – Leo Zhang
          Jan 2 at 15:53




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53999891%2fhow-to-throw-an-exception-with-callstack%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          MongoDB - Not Authorized To Execute Command

          Npm cannot find a required file even through it is in the searched directory

          in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith