-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | utility to add extra safety to monadic returns -- -- The Final library makes it possible to point out return -- values in (monadic) functions. This adds extra compile-time safety to -- your code, because the compiler will warn you if marked return values -- accidentally became middle parts of a big function written with do -- notation. -- -- Documentation and examples provided in the Control.Final -- module's own documentation. @package final @version 0.1 -- | The Final library makes it possible to point out return -- values in (monadic) functions. This adds extra compile-time safety to -- your code, because the compiler will warn you if marked return values -- accidentally became middle parts of a big function written with do -- notation. -- -- A worked out example can be found in Control.Final.Example, and -- an alternative approach is considered in -- Control.Final.Alternatives. module Control.Final -- | Use this instead of return when you want to mark this point as -- a final return for the corresponding runFinal. final :: (Monad m, FinalClass f) => x -> m (f x) -- | Most commonly used at the beginning of functions to mark the point in -- the code where final calls "return to". -- -- This compiles: -- --
--   simple = runFinal $ do
--     print "line1"
--     do return False
--        return True
--     final True
--   
-- -- This fails at the first "final True": -- --
--   simple = runFinal $ do
--     print "line1"
--     do return False
--        final True
--     final True
--   
runFinal :: Monad m => m (Final a) -> m a -- | Useful when the final return value is a monadic computation itself -- that after returned has to be joined into the current computation. joinFinal :: Monad m => m (Final (m a)) -> m a -- | A version of atomically, that joins and runFinals. This is the most -- common usage for the author. Details and example can be found in -- Control.Final.Example. atomicJoinFinal :: (Functor m, MonadIO m) => STM (Final (m a)) -> m a -- | MonadIO version of atomically. Not really related to Final, -- but if we already have atomicJoinFinal in this module, it makes -- sense to provide this too. atomic :: MonadIO m => STM a -> m a -- | This class is the main idea behind Final. finalV is -- in a type class, so we we can have to instances, therefore if used -- incorrectly the compiler sees an ambiguity. class FinalClass f finalV :: FinalClass f => a -> f a -- | Usage example: -- --
--   pureExample p =
--     runFinalV $ case p of
--       True -> finalV "quux"
--       False -> finalV "foobar"
--   
runFinalV :: Final a -> a instance FinalClass FinalTooManyReturns instance FinalClass Final -- | An alternative to Control.Final is a using the writer monad. -- --
--   instance Monoid a => Monoid (IO a) where
--     mempty = return mempty
--     mappend = liftA2 mappend
--   
--   stmExampleW = do
--     tv <- newTVarIO "xxx"
--     join $ atomically $ execWriterT $ do
--       val <- lift $ readTVar tv
--       case val of
--         "xxx" -> do
--           lift $ writeTVar tv "foobar"
--           tell $ print "it was xxx"
--         "yyy" -> do
--           lift $ writeTVar tv "quux"
--           tell $ print "it was yyy"
--       tell $ print "I finished"
--   
-- -- This works. Unfortunately when using the writer, you have to -- lift every operation of the base monad and also there is no -- such thing as finalTell to specify that this is a return -- point of the function. On the positive side, the writer alternative -- let's you gather multiple IO operations together using a -- Monoid, while final can't do that. module Control.Final.Alternatives -- | Consider this peace of code: -- --
--   join $ atomically $ do
--     radarPositive <- readTVar radarTVar
--     launchKeyInserted <- readTVar launchKeyTVar
--     case radarPositive of
--       False -> do
--         modifyTVar radarNegativeCounter (+1)
--         return $ print "No need for missiles, it's peaceful"
--       True -> do
--         modifyTVar radarPositiveCounter (+1)
--         case launchKeyInserted of
--           False -> do
--             modifyTVar keyMissingCounter (+1)
--             return $ print "No launch key, ignoring radar"
--           True -> do
--             modifyTVar launchCounter (+1)
--             return $ launchMissiles
--     return $ print "extra debug: state checking finished"
--   
-- -- We use STM to make state checking of multiple TVars one -- atomic transaction. Since we can't do IO in STM, we are -- just returning the IO that needs to be done in the different -- cases. Unfortunately when we try to add the extra debugging as the -- last statement, that silently ignores all the previous "return" -- values, even launchMissiles. -- -- On the other hand when using final: -- --
--   atomicJoinFinal $ do
--     radarPositive <- readTVar radarTVar
--     launchKeyInserted <- readTVar launchKeyTVar
--     case radarPositive of
--       False -> do
--         modifyTVar radarNegativeCounter (+1)
--         final $ print "No need for missiles, it's peaceful"
--       True -> do
--         modifyTVar radarPositiveCounter (+1)
--         case launchKeyInserted of
--           False -> do
--             modifyTVar keyMissingCounter (+1)
--             final $ print "No launch key, ignoring radar"
--           True -> do
--             modifyTVar launchCounter (+1)
--             final $ launchMissiles
--     final $ print "extra debug: state checking finished"
--   
-- -- We get a compile error that contains this: -- --
--   Note: there are several potential instances:
--     instance FinalClass Control.Final.FinalTooManyReturns
--   
-- -- Internally Final is based on ambiguity checking in the type -- system. The prohibited ambiguity occurs, because the only way to -- decide what final means is by matching it to the corresponding -- atomicJoinFinal. This is now only possible for the final -- at the end of the function and not for the middle ones, so we get the -- error. module Control.Final.Example