最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

callstack - How to add `HasCallStack` to a custom monad in Haskell? - Stack Overflow

programmeradmin0浏览0评论

I can use a type alias to add HasCallStack to a monad e.g.:

type XIO' a = HasCallStack => IO a

which saves me some keystrokes and I don't have to remember about putting HasCallStack everywhere.

However, when I define a custom datatype like that:

data XIO a = HasCallStack => XIO { unXIO :: IO a }

The call stacks aren't added to functions of type XIO. Is there a way to add a HasCallStack constraint automatically to a custom monad? I've also tried adding it to all functions in the instances using InstanceSigs but it made no difference. A full example:

module Main (main) where

import RIO

main :: IO ()
main = do
  catchAny (unXIO $ f1) $ \ex -> do
    putStrLn "::: An exception without callstack"
    putStrLn $ displayException ex

  catchAny (f1') $ \ex -> do
    putStrLn "::: An exception with callstack"
    putStrLn $ displayException ex

f1' :: XIO' ()
f1' = do
  pure ()
  f2'
  pure ()

f1 :: XIO ()
f1 = do
  pure ()
  f2
  pure ()

f2' :: XIO' ()
f2' = do
  pure ()
  _ <- throwIO $ stringException "Hello"
  pure ()

f2 :: XIO ()
f2 = do
  pure ()
  _ <- throwIO $ stringException "Hello"
  pure ()

type XIO' a = HasCallStack => IO a

data XIO a = HasCallStack => XIO { unXIO :: IO a }

instance Functor XIO where
  fmap f (XIO m) = XIO $ fmap f m

instance Applicative XIO where
  pure a = XIO $ pure a
  XIO f <*> XIO a = XIO $ f <*> a

instance Monad XIO where
  return = pure
  XIO m >>= f = XIO $ m >>= (\k -> unXIO $ f k)

instance MonadIO XIO where
  liftIO m = XIO $ m

I can use a type alias to add HasCallStack to a monad e.g.:

type XIO' a = HasCallStack => IO a

which saves me some keystrokes and I don't have to remember about putting HasCallStack everywhere.

However, when I define a custom datatype like that:

data XIO a = HasCallStack => XIO { unXIO :: IO a }

The call stacks aren't added to functions of type XIO. Is there a way to add a HasCallStack constraint automatically to a custom monad? I've also tried adding it to all functions in the instances using InstanceSigs but it made no difference. A full example:

module Main (main) where

import RIO

main :: IO ()
main = do
  catchAny (unXIO $ f1) $ \ex -> do
    putStrLn "::: An exception without callstack"
    putStrLn $ displayException ex

  catchAny (f1') $ \ex -> do
    putStrLn "::: An exception with callstack"
    putStrLn $ displayException ex

f1' :: XIO' ()
f1' = do
  pure ()
  f2'
  pure ()

f1 :: XIO ()
f1 = do
  pure ()
  f2
  pure ()

f2' :: XIO' ()
f2' = do
  pure ()
  _ <- throwIO $ stringException "Hello"
  pure ()

f2 :: XIO ()
f2 = do
  pure ()
  _ <- throwIO $ stringException "Hello"
  pure ()

type XIO' a = HasCallStack => IO a

data XIO a = HasCallStack => XIO { unXIO :: IO a }

instance Functor XIO where
  fmap f (XIO m) = XIO $ fmap f m

instance Applicative XIO where
  pure a = XIO $ pure a
  XIO f <*> XIO a = XIO $ f <*> a

instance Monad XIO where
  return = pure
  XIO m >>= f = XIO $ m >>= (\k -> unXIO $ f k)

instance MonadIO XIO where
  liftIO m = XIO $ m
Share asked Mar 3 at 20:23 carbolymercarbolymer 1,6522 gold badges17 silver badges32 bronze badges 1
  • 1 Note that, even if it were possible, those HasCallStacks could likely cause memory leaks for code that recurses a lot. – danidiaz Commented Mar 3 at 20:30
Add a comment  | 

1 Answer 1

Reset to default 1

I can't think of a way to associate the HasCallStack constraint with a data type. Because of the way it's implemented, I think it needs to be part of each function signature that should contribute to the call stack, either specified directly or as part of a type alias.

So, your best bet is to define your custom monad as an internal type and then define a type alias for use in function signatures, like:

newtype XIO_internal a = XIO { unXIO :: IO a }
type XIO a = HasCallStack => XIO_internal a

For example:

import RIO

newtype XIO_internal a = XIO { unXIO :: IO a }
  deriving (Functor, Applicative, Monad, MonadIO)
type XIO a = HasCallStack => XIO_internal a

main :: IO ()
main = do
  catchAny (unXIO $ f1 2) $ \ex -> do
    putStrLn $ displayException ex

f1 :: Int -> XIO ()
f1 x = do
  f2 (x*2)

f2 :: Int -> XIO ()
f2 x = do
  liftIO $ print x
  _ <- throwIO $ stringException "Hello"
  pure ()
发布评论

评论列表(0)

  1. 暂无评论