Exception 优化和测试错误调用之间的交互

Exception 优化和测试错误调用之间的交互,exception,optimization,haskell,ghc,hunit,Exception,Optimization,Haskell,Ghc,Hunit,我在一个模块中有一个函数,如下所示: module MyLibrary (throwIfNegative) where throwIfNegative :: Integral i => i -> String throwIfNegative n | n < 0 = error "negative" | otherwise = "no worries" 它可以正常工作,但在进行优化编译时会失败: $ ghc --make -O Test.h

我在一个模块中有一个函数,如下所示:

module MyLibrary (throwIfNegative) where

throwIfNegative :: Integral i => i -> String
throwIfNegative n | n < 0 = error "negative"
                  | otherwise = "no worries"
它可以正常工作,但在进行优化编译时会失败:

$ ghc --make -O Test.hs
$ ./Test
### Failure:                              
must throw when given a negative number
Cases: 1  Tried: 1  Errors: 0  Failures: 1
我不知道这里发生了什么。似乎尽管我使用了
evaluate
,但函数没有得到评估。此外,如果我执行以下任何步骤,它也会再次工作:

  • 删除它并直接调用代码
  • 通过fnegative将
    移动到与测试用例相同的模块
  • 通过fnegative删除
    的类型签名

我认为这是因为它导致优化的应用方式不同。有什么建议吗?

优化、严格,可能有点棘手

重现上述问题的最简单方法是使用
上的
NOINLINE
(函数也没有跨模块边界内联):

然后在案例监查器之外,对Throwiferor发出调用:

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
或者一个爆炸模式:

case_negative =
    handleJust errorCalls (const $ return ()) $ do
        let !x = throwIfNegative (-1)
        assertFailure "must throw when given a negative number"
  where errorCalls (ErrorCall _) = Just ()
所以我认为我们应该看看
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
那是一本很有趣的书

我认为GHC在这里做了一些可疑的事情,建议不要使用
evaluate
(而是直接使用
seq
)。这需要更多地思考GHC在严格性方面的做法


我必须帮助GHC总部做出决定。

我可以复制这一点。有趣!另外,如果在模块中包含
throwIfNegative
,并用
NOINLINE
标记,则会失败。非常有趣。我会注意跟踪的。
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
case_negative =
    handleJust errorCalls (const $ return ()) $ do
        throwIfNegative (-1) `seq` assertFailure "must throw when given a negative number"
  where errorCalls (ErrorCall _) = Just ()
case_negative =
    handleJust errorCalls (const $ return ()) $ do
        let !x = throwIfNegative (-1)
        assertFailure "must throw when given a negative number"
  where errorCalls (ErrorCall _) = Just ()
-- | 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