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

  • Home
  • SEARCH
  • 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 4346532
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 21, 20262026-05-21T12:11:09+00:00 2026-05-21T12:11:09+00:00

I have a function in a module that looks something like this: module MyLibrary

  • 0

I have a function in a module that looks something like this:

module MyLibrary (throwIfNegative) where

throwIfNegative :: Integral i => i -> String
throwIfNegative n | n < 0 = error "negative"
                  | otherwise = "no worries"

I could of course return Maybe String or some other variant, but I think it’s fair to say that it’s a programmer error to call this function with a negative number so using error is justified here.

Now, since I like having my test coverage at 100% I want to have a test case that checks this behavior. I have tried this

import Control.Exception
import Test.HUnit

import MyLibrary

case_negative =
    handleJust errorCalls (const $ return ()) $ do
        evaluate $ throwIfNegative (-1)
        assertFailure "must throw when given a negative number"
  where errorCalls (ErrorCall _) = Just ()

main = runTestTT $ TestCase case_negative

and it sort of works, but it fails when compiling with optimizations:

$ ghc --make -O Test.hs
$ ./Test
### Failure:                              
must throw when given a negative number
Cases: 1  Tried: 1  Errors: 0  Failures: 1

I’m not sure what’s happening here. It seems like despite my use of evaluate, the function does not get evaluated. Also, it works again if I do any of these steps:

  • Remove HUnit and call the code directly
  • Move throwIfNegative to the same module as the test case
  • Remove the type signature of throwIfNegative

I assume this is because it causes the optimizations to be applied differently. Any pointers?

  • 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-05-21T12:11:10+00:00Added an answer on May 21, 2026 at 12:11 pm

    Optimizations, strictness, and imprecise exceptions can be a bit tricky.

    The easiest way to reproduce this problem above is with a NOINLINE on throwIfNegative (the function isn’t being inlined across module boundaries either):

    import Control.Exception
    import Test.HUnit
    
    throwIfNegative :: Int -> String
    throwIfNegative n | n < 0     = error "negative"
                      | otherwise = "no worries"
    {-# NOINLINE throwIfNegative #-}
    
    case_negative =
        handleJust errorCalls (const $ return ()) $ do
            evaluate $ throwIfNegative (-1)
            assertFailure "must throw when given a negative number"
      where errorCalls (ErrorCall _) = Just ()
    
    main = runTestTT $ TestCase case_negative
    

    Reading the core, with optimizations on, the GHC inlines evaluate properly (?):

    catch#
          @ ()
          @ SomeException
          (\ _ ->
             case throwIfNegative (I# (-1)) of _ -> ...
    

    and then floats out the call to throwIfError, outside of the case scrutinizer:

    lvl_sJb :: String
    lvl_sJb = throwIfNegative lvl_sJc
    
    lvl_sJc = I# (-1)
    
    throwIfNegative =
      \ (n_adO :: Int) ->
        case n_adO of _ { I# x_aBb ->
          case <# x_aBb 0 of _ {
             False -> lvl_sCw; True -> error lvl_sCy
    

    and strangely, at this point, no other code now calls lvl_sJb, so the entire test becomes dead code, and is stripped out — GHC has determined that it is unused!

    Using seq instead of evaluate is happy enough:

    case_negative =
        handleJust errorCalls (const $ return ()) $ do
            throwIfNegative (-1) `seq` assertFailure "must throw when given a negative number"
      where errorCalls (ErrorCall _) = Just ()
    

    or a bang pattern:

    case_negative =
        handleJust errorCalls (const $ return ()) $ do
            let !x = throwIfNegative (-1)
            assertFailure "must throw when given a negative number"
      where errorCalls (ErrorCall _) = Just ()
    

    so I think we should look at the semantics of evaluate:

    -- | Forces its argument to be evaluated to weak head normal form when
    -- the resultant 'IO' action is executed. It can be used to order
    -- evaluation with respect to other 'IO' operations; its semantics are
    -- given by
    --
    -- >   evaluate x `seq` y    ==>  y
    -- >   evaluate x `catch` f  ==>  (return $! x) `catch` f
    -- >   evaluate x >>= f      ==>  (return $! x) >>= f
    --
    -- /Note:/ the first equation implies that @(evaluate x)@ is /not/ the
    -- same as @(return $! x)@.  A correct definition is
    --
    -- >   evaluate x = (return $! x) >>= return
    --
    evaluate :: a -> IO a
    evaluate a = IO $ \s -> let !va = a in (# s, va #) -- NB. see #2273
    

    That #2273 bug is a pretty interesting read.

    I think GHC is doing something suspicious here, and recommend not using evalaute (instead, use seq directly). This needs more thinking about what GHC is doing with the strictness.

    I’ve filed a bug report to help get a determination from GHC HQ.

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

Sidebar

Related Questions

I have a function that looks like this class NSNode { function insertAfter(NSNode $node)
I have a function that looks like the following, with a whole lot of
I have a function where I need to do something to a string. I
I have a function that gives me the following warning: [DCC Warning] filename.pas(6939): W1035
I have a function that gets x(a value) and xs(a list) and removes all
I have a function that takes, amongst others, a parameter declared as int privateCount
I have a function in a native DLL defined as follows: #include <string> void
I have a function, parseQuery, that parses a SQL query into an abstract representation
I have a function that I use called sqlf(), it emulates prepared statements. For
I have a function that is effectively a replacement for print, and I want

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.