-- 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