If the value evaluated with hasX contains multiple exceptions, only the first one encountered will determine the outcome of hasX.
>>> let a = errorX "XException" :> error "ErrorCall" :> Nil
>>> let b = error "ErrorCall" :> errorX "XException" :> Nil
>>> hasX a
Left "X: XException\nCallStack (from HasCallStack):\n errorX, called at <interactive>:1:9 in interactive:Ghci2"
>>> hasX b
*** Exception: ErrorCall
CallStack (from HasCallStack):
error, called at <interactive>:2:9 in interactive:Ghci3
In my opinion, both should have thrown an ErrorCall. The documentation for hasX describes it as fully evaluate a value. It is currently not evaluating anything beyond the XException.
Currently, hasX reduces its argument to normal form using NFData, and catches XExceptions. This checks only one of the exceptions the argument might throw. Instead, we probably need to first check for exceptions other than XException and only after that check for all exceptions (which boils down to just the XExceptions of course):
hasX :: (NFDataX a, NFData a) => a -> Either String a
hasX a =
unsafeDupablePerformIO
(catch
(evaluate (rnfX a `seq` rnf a) >> return (Right a))
(\(XException msg) -> return (Left msg)))
If the value evaluated with
hasXcontains multiple exceptions, only the first one encountered will determine the outcome ofhasX.In my opinion, both should have thrown an
ErrorCall. The documentation forhasXdescribes it as fully evaluate a value. It is currently not evaluating anything beyond theXException.Currently,
hasXreduces its argument to normal form usingNFData, and catchesXExceptions. This checks only one of the exceptions the argument might throw. Instead, we probably need to first check for exceptions other thanXExceptionand only after that check for all exceptions (which boils down to just theXExceptions of course):