Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8150087
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 6, 20262026-06-06T14:58:53+00:00 2026-06-06T14:58:53+00:00

The question is similar to this question. However, this one is about exceptions, not

  • 0

The question is similar to this question. However, this one is about exceptions, not about lazy I/O.

Here is a test:

{-# LANGUAGE ScopedTypeVariables #-}

import Prelude hiding ( catch )
import Control.Exception

fooLazy :: Int -> IO Int
fooLazy m = return $ 1 `div` m

fooStrict :: Int -> IO Int
fooStrict m = return $! 1 `div` m

test :: (Int -> IO Int) -> IO ()
test f = print =<< f 0 `catch` \(_ :: SomeException) -> return 42

testLazy :: Int -> IO Int
testLazy m = (return $ 1 `div` m) `catch` \(_ :: SomeException) -> return 42

testStrict :: Int -> IO Int
testStrict m = (return $! 1 `div` m) `catch` \(_ :: SomeException) -> return 42

So I wrote two functions fooLazy which is lazy and fooStrict which is strict, also there is two tests testLazy and testStrict, then I try to catch division by zero:

> test fooLazy
*** Exception: divide by zero
> test fooStrict
42
> testLazy 0
*** Exception: divide by zero
> testStrict 0
42

and it fails in lazy cases.

The first thing that comes to mind is to write a version of the catch function that force the evaluation on its first argument:

{-# LANGUAGE ScopedTypeVariables #-}

import Prelude hiding ( catch )
import Control.DeepSeq
import Control.Exception
import System.IO.Unsafe

fooLazy :: Int -> IO Int
fooLazy m = return $ 1 `div` m

fooStrict :: Int -> IO Int
fooStrict m = return $! 1 `div` m

instance NFData a => NFData (IO a) where
  rnf = rnf . unsafePerformIO

catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
catchStrict = catch . force

test :: (Int -> IO Int) -> IO ()
test f = print =<< f 0 `catchStrict` \(_ :: SomeException) -> return 42

testLazy :: Int -> IO Int
testLazy m = (return $ 1 `div` m) `catchStrict` \(_ :: SomeException) -> return 42

testStrict :: Int -> IO Int
testStrict m = (return $! 1 `div` m) `catchStrict` \(_ :: SomeException) -> return 42

it seems to work:

> test fooLazy
42
> test fooStrict
42
> testLazy 0
42
> testStrict 0
42

but I use the unsafePerformIO function here and this is scary.

I have two questions:

  1. Can one be sure that the catch function always catches all exceptions, regardless of the nature of it first argument?
  2. If not, is there a well-known way to deal with this kind of problems? Something like the catchStrict function is suitable?

UPDATE 1.

This is a better version of the catchStrict function by nanothief:

forceM :: (Monad m, NFData a) => m a -> m a
forceM m = m >>= (return $!) . force

catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
catchStrict expr = (forceM expr `catch`)

UPDATE 2.

Here is another ‘bad’ example:

main :: IO ()
main = do
  args <- getArgs
  res <- return ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> return 0
  print res

It should be rewritten like this:

main :: IO ()
main = do
  args <- getArgs
  print ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> print 0
-- or
-- 
-- res <- return ((+ 1) $ read $ head args) `catchStrict` \(_ :: SomeException) -> return 0
-- print res
-- 
-- or
-- 
-- res <- returnStrcit ((+ 1) $ read $ head args) `catch` \(_ :: SomeException) -> return 0
-- print res
-- 
-- where
returnStrict :: Monad m => a -> m a
returnStrict = (return $!)

UPDATE 3.

As nanothief noticed, there is no guarantee that the catch function always catch any exception. So one need to use it carefully.

Few tips on how to solve related problems:

  1. Use ($!) with return, use forceM on the first argument of catch, use the catchStrict function.
  2. I also noticed that sometimes people add some strictness to instances of their transformers.

Here is an example:

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeSynonymInstances, FlexibleInstances
  , MultiParamTypeClasses, UndecidableInstances, ScopedTypeVariables #-}

import System.Environment

import Prelude hiding ( IO )
import qualified Prelude as P ( IO )
import qualified Control.Exception as E
import Data.Foldable
import Data.Traversable
import Control.Applicative
import Control.Monad.Trans
import Control.Monad.Error

newtype StrictT m a = StrictT { runStrictT :: m a } deriving
  ( Foldable, Traversable, Functor, Applicative, Alternative, MonadPlus, MonadFix
  , MonadIO
  )

instance Monad m => Monad (StrictT m) where
  return = StrictT . (return $!)
  m >>= k = StrictT $ runStrictT m >>= runStrictT . k
  fail = StrictT . fail

instance MonadTrans StrictT where
  lift = StrictT

type IO = StrictT P.IO

instance E.Exception e => MonadError e IO where
  throwError = StrictT . E.throwIO
  catchError m h = StrictT $ runStrictT m `E.catch` (runStrictT . h)

io :: StrictT P.IO a -> P.IO a
io = runStrictT

It is essentially the identity monad transformer, but with strict return:

foo :: Int -> IO Int
foo m = return $ 1 `div` m

fooReadLn :: Int -> IO Int
fooReadLn x = liftM (`div` x) $ liftIO readLn

test :: (Int -> IO Int) -> P.IO ()
test f = io $ liftIO . print =<< f 0 `catchError` \(_ :: E.SomeException) -> return 42

main :: P.IO ()
main = io $ do
  args <- liftIO getArgs
  res <- return ((+ 1) $ read $ head args) `catchError` \(_ :: E.SomeException) -> return 0
  liftIO $ print res

-- > test foo
-- 42
-- > test fooReadLn
-- 1
-- 42
-- ./main
-- 0
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-06T14:58:54+00:00Added an answer on June 6, 2026 at 2:58 pm

    Firstly (I’m not sure if you know this already), the reason the catch doesn’t work with the lazy case is the

    1 `div` 0
    

    expression isn’t evaluated until it is needed, which is inside the print function. However, the catch method is applied just to the f 0 expression, not the whole print =<< f 0 expression, so the exception isn’t caught. If you did:

    test f = (print =<< f 0) `catch` \(_ :: SomeException) -> print 42
    

    instead, it works correctly in both cases.

    If you want to make a catch statement though that forces complete evaluation of the IO result, instead of making a new instance of NFData, you could write a forceM method, and use that in the catchStrict method:

    forceM :: (Monad m, NFData a) => m a -> m a
    forceM m = m >>= (return $!) . force
    
    catchStrict :: (Exception e, NFData a) => IO a -> (e -> IO a) -> IO a
    catchStrict expr = (forceM expr `catch`)
    

    (I’m a bit surprised that forceM isn’t inside the Control.DeepSeq library)


    Regarding your comment:

    No, the rule is the exception is only thrown when the value is computed, and that is only done when it is needed by haskell. And if haskell can delay the evaluation of something it will.

    An example test function that doesn’t use $!, but still causes an exception straight away (so the normal catch will catch the divide by zero exception) is:

    fooEvaluated :: Int -> IO Int
    fooEvaluated m = case 3 `div` m of
      3 -> return 3
      0 -> return 0
      _ -> return 1
    

    Haskell is forced to evaluated the “3 `div` m” expression, as it needs to match the result against 3 and 0.

    As a last example, the following doesn’t throw any exception, and when used with the test function returns 1:

    fooNoException :: Int -> IO Int
    fooNoException m = case 3 `div` m of
      _ -> return 1
    

    This is because haskell never needs to calculate “3 `div` m” expression (as _ matches everything), so it is never calculated, hence no exception is thrown.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I found a similar question about this but aimed at Debian here . However
I posted a question similar to this earlier, however, after thinking about it and
I've already checked this.. similar question but I am not convinced about the answers...
Question is similar to this (unanswered) and this one (same problem not involving Git).
I know this sounds strange earlier I asked about similar question in one of
Similar to this question: link However I have already mastered that. My problem is
This question is similar to GWT Table that supports sorting, scrolling and filtering However
This is similar to a question that has already been asked. However, I am
I found this similar question here , but this is really old. Was it
This question is similar to this one How do I add options to a

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.