{-# LANGUAGE CPP #-}
{-# OPTIONS_GHC -Wall -fwarn-tabs #-}
----------------------------------------------------------------
--                                                  ~ 2009.05.13
-- |
-- Module      :  Control.Exception.Extras
-- Copyright   :  Copyright (c) 2007--2009 wren ng thornton
-- License     :  BSD3
-- Maintainer  :  wren@community.haskell.org
-- Stability   :  experimental
-- Portability :  vaguely portable (with CPP)
--
-- This module provides additional combinators for "Control.Exception",
-- namely for pure computations.
--
-- 'Prelude.error' throws @ErrorCall@ which is a @Control.Monad.Exception@,
-- whereas @Prelude.catch@ only catches 'IOError'. "Control.Exception"
-- is non-Haskell98 and so technically non-portable, but it's
-- supported by all extant compilers as of early 2009.
--
-- In GHC 6.10 the switch was made to extensible exceptions, which
-- may not be portable to other compilers. We use CPP to distinguish
-- between the older GHC 6.8 exceptions and the new ones, but we
-- deal with them all the same.
----------------------------------------------------------------
module Control.Exception.Extras (safely) where

import Prelude hiding (catch) -- only catches 'IOError's not 'ErrorCall's

#if __GLASGOW_HASKELL__ >= 610
import Control.Exception (catch, evaluate, SomeException)
#else
import Control.Exception (catch, evaluate)
#endif

import System.IO.Unsafe 
----------------------------------------------------------------

-- The trickiest thing to watch out for is that you must strictly
-- evaluate the expression or else the return/evaluate function
-- will just lazily thunk up the expression. Which means that when
-- you finally run the IO, you'll have already stripped off the IO
-- wrapper that can catch the exception prior to evaluating the
-- expression (which then throws an exception out past the catcher).
--
-- | Apply a function safely, capturing all errors as 'Nothing'.
-- Since we don't look at the exception thrown, this is continuous
-- (and so fairly okay). But since there's no way to catch infinite
-- loops, there's still an indeterminism about catching an exception
-- vs looping forever (so this technically isn't safe in the strictest
-- possible sense).
--
-- See also <http://www.haskell.org/pipermail/haskell-cafe/2009-March/057778.html>
infixr 0 `safely`
{-# NOINLINE safely #-}
safely  :: (a -> b) -> (a -> Maybe b)
#if __GLASGOW_HASKELL__ >= 610
safely f x = unsafePerformIO
           $ catch (evaluate $! Just $! f x)
                   (\e -> const (return Nothing) (e :: SomeException))
#else
safely f x = unsafePerformIO
           $ catch (evaluate $! Just $! f x)
                   (\_ -> return Nothing)
#endif

----------------------------------------------------------------
----------------------------------------------------------- fin.
