packages feed

oops-examples (empty) → 0.2.0.0

raw patch · 3 files changed

+395/−0 lines, 3 filesdep +basedep +exceptionsdep +mtl

Dependencies added: base, exceptions, mtl, oops, text, transformers

Files

+ LICENSE view
@@ -0,0 +1,21 @@+Copyright (c) 2023 John Ky+Copyright (c) 2019 Tom Harding++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ oops-examples.cabal view
@@ -0,0 +1,45 @@+cabal-version: 3.0++name:                   oops-examples+version:                0.2.0.0+synopsis:               Oops examples+description:            Oops examples.+homepage:               https://www.github.com/haskell-works/oops+license:                MIT+license-file:           LICENSE+author:                 John Ky+maintainer:             newhoggy@gmail.com+copyright:              2023 John Ky+                        2019 Tom Harding+category:               Data+                        Control+tested-with:            GHC == 9.4.4, GHC == 9.2.5, GHC == 9.0.2, GHC == 8.10.7++source-repository head+  type: git+  location: https://github.com/haskell-works/oops++common base                           { build-depends: base                           >= 4.12       && < 5      }++common exceptions                     { build-depends: exceptions                                               }+common mtl                            { build-depends: mtl                                                      }+common oops                           { build-depends: oops                                                     }+common text                           { build-depends: text                                                     }+common transformers                   { build-depends: transformers                                             }++common project-config+  default-language:     Haskell2010+  default-extensions:   BlockArguments+                        RankNTypes+                        ScopedTypeVariables+  ghc-options:          -Wall++library+  import:               base, project-config,+                        exceptions,+                        mtl,+                        oops,+                        text,+                        transformers,+  exposed-modules:      Examples+  hs-source-dirs:       src
+ src/Examples.hs view
@@ -0,0 +1,329 @@+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE MonoLocalBinds #-}+{-# LANGUAGE DataKinds #-}++module Examples where++import Control.Monad.Except (MonadError, ExceptT, runExceptT)+import Control.Monad.Oops ( CouldBe, Variant )+import Data.Text (Text)+import Text.Read (readMaybe)+import Data.Functor.Identity (Identity)+import Control.Monad.Trans (MonadIO)+import Data.Function ((&))++import qualified Control.Monad.Oops as OO+import qualified System.IO as IO+import Control.Monad.IO.Class (MonadIO(..))+import Control.Exception (IOException)+import Control.Monad.Catch (MonadCatch)++-- | A simple function that throws an error.+--+-- The type is the one that is inferred by GHC.+readIntV1 :: ()+  => MonadError (Variant e) m+  => OO.CouldBeF e Text+  => String+  -> m Int+readIntV1 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- | A simple function that throws an error.+--+-- This is the same as before, but we can rewrite constraint on 'e' differently.+readIntV2 :: ()+  => MonadError (Variant e) m+  => e `CouldBe` Text+  => String+  -> m Int+readIntV2 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- | A simple function that throws an error.+--+-- We can also use 'ExceptT'+readIntV3 :: ()+  => e `CouldBe` Text+  => String+  -> ExceptT (Variant e) Identity Int+readIntV3 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- | A simple IO function that throws an error.+--+-- We can also use 'ExceptT' of 'IO'.+readIntV4 :: ()+  => e `CouldBe` Text+  => String+  -> ExceptT (Variant e) IO Int+readIntV4 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- | A simple function that throws an error.+--+-- Or use MonadIO instead of IO directly.+readIntV5 :: ()+  => MonadError (Variant e) m+  => MonadIO m+  => e `CouldBe` Text+  => String+  -> m Int+readIntV5 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- | A simple function that throws an error.+--+-- Or use 'MonadIO' with 'ExceptT'.+readIntV6 :: ()+  => MonadIO m+  => e `CouldBe` Text+  => String+  -> ExceptT (Variant e) m Int+readIntV6 s = case readMaybe @Int s of+  Just i -> return i+  Nothing -> OO.throw @Text "Not an integer"++-- We can represent each error as a separate type.++data NotAnInteger = NotAnInteger++data NotPositive = NotPositive++-- | A simple function can throw two errors+readPositiveInt1 :: ()+  => MonadIO m+  => e `CouldBe` NotAnInteger+  => e `CouldBe` NotPositive+  => String+  -> ExceptT (Variant e) m Int+readPositiveInt1 s =+  case readMaybe @Int s of+    Just i ->+      if i > 0+        then return i+        else OO.throw NotPositive+    Nothing -> OO.throw NotAnInteger++-- We can call a function that throws an error of type 'Text' and allow the+-- error to propagate by declaring we also throw an error of type 'Text'.+example1 :: ()+  => e `CouldBe` Text+  => String+  -> ExceptT (Variant e) IO Int+example1 s = do+  i <- readInt s+  liftIO $ IO.print i+  return i+  where+    readInt :: ()+      => e `CouldBe` Text+      => String+      -> ExceptT (Variant e) IO Int+    readInt = error "unimplemented"++-- Or alternatively, we can catch the error of type 'Text' and handle it.+-- in which case the error doesn't propagate.  Notice the 'e CouldBe Text'+-- constraint is not needed in this case.+example2 :: ()+  => String+  -> ExceptT (Variant e) IO Int+example2 s = do+  i <- readInt s+    & OO.catch @Text (\_ -> pure 0)+  liftIO $ IO.print i+  return i+  where+    readInt :: ()+      => e `CouldBe` Text+      => String+      -> ExceptT (Variant e) IO Int+    readInt = error "unimplemented"++-- When we don't throw any errors, we can use the 'runOops' function to+-- convert the 'ExceptT' to an 'IO' action.+example3 :: ()+  => String+  -> IO Int+example3 s = OO.runOops $ do+  i <- readInt s+    & OO.catch @Text (\_ -> pure 0)+  liftIO $ IO.print i+  return i+  where+    readInt :: ()+      => e `CouldBe` Text+      => String+      -> ExceptT (Variant e) IO Int+    readInt = error "unimplemented"++data FileNotFound = FileNotFound++data FileNotReadable = FileNotReadable++data Errors1+  = Errors1NotPositive NotPositive+  | Errors1NotAnInteger NotAnInteger++-- We can call a function that throws multiple errors into a function+-- that only throws one by just catching and rethrowing the one+-- error.+example4 :: ()+  => MonadIO m+  => e `CouldBe` Errors1+  => String+  -> ExceptT (Variant e) m Int+example4 s = do+  i <- readPositiveInt1 s+    & OO.catch @NotPositive (OO.throw . Errors1NotPositive)+    & OO.catch @NotAnInteger (OO.throw . Errors1NotAnInteger)+  liftIO $ IO.print i+  return i++--------------------------------------------------------------------------------+-- Embedding 'Oops' into vanilla 'ExceptT' and 'Either' code.++-- We we have a function that only throws one error, we can use 'runOopsInExceptT'+-- to remove the 'Variant' wrapper leaving only the 'ExceptT'.+example5 :: ()+  => MonadIO m+  => String+  -> ExceptT Errors1 m Int+example5 s = OO.runOopsInExceptT $ do+  i <- readPositiveInt1 s+    & OO.catch @NotPositive (OO.throw . Errors1NotPositive)+    & OO.catch @NotAnInteger (OO.throw . Errors1NotAnInteger)+  liftIO $ IO.print i+  return i++-- We we have a function that only throws one error, we can use 'runOopsInEither'+-- to remove the 'Variant' wrapper and the 'Except' leaving only the 'Either'.+example6 :: ()+  => MonadIO m+  => String+  -> m (Either Errors1 Int)+example6 s = OO.runOopsInEither $ do+  i <- readPositiveInt1 s+    & OO.catch @NotPositive (OO.throw . Errors1NotPositive)+    & OO.catch @NotAnInteger (OO.throw . Errors1NotAnInteger)+  liftIO $ IO.print i+  return i++--------------------------------------------------------------------------------+-- Embedding vanilla 'ExceptT' and 'Either' code into 'Oops' code.+-- We can call a function that throws multiple errors into a function+-- that only throws one by just catching and rethrowing the one+-- error.++-- We can call a function that throws vanilla 'ExceptT' errors and use 'onLeft'+-- to catch and rethrow the errors as oops errors.+example7 :: ()+  => MonadIO m+  => e `CouldBe` NotPositive+  => e `CouldBe` NotAnInteger+  => String+  -> ExceptT (Variant e) m Int+example7 s = do+  i <- readInt s+    & OO.onLeft OO.throw+  pos <- requirePositive i+    & OO.onLeft OO.throw+  liftIO $ IO.print pos+  return pos+  where+    readInt :: MonadIO m => String -> m (Either NotAnInteger Int)+    readInt = error "unimplemented"+    requirePositive :: MonadIO m => Int -> m (Either NotPositive Int)+    requirePositive = error "unimplemented"++-- 'onLeftThrow' is shorthand for 'onLeft throw'.+example8 :: ()+  => MonadIO m+  => e `CouldBe` NotPositive+  => e `CouldBe` NotAnInteger+  => String+  -> ExceptT (Variant e) m Int+example8 s = do+  i <- readInt s+    & OO.onLeftThrow+  pos <- requirePositive i+    & OO.onLeftThrow+  liftIO $ IO.print pos+  return pos+  where+    readInt :: MonadIO m => String -> m (Either NotAnInteger Int)+    readInt = error "unimplemented"+    requirePositive :: MonadIO m => Int -> m (Either NotPositive Int)+    requirePositive = error "unimplemented"++-- We can similarly call a function that throws via vanilla 'ExceptT'+example9 :: ()+  => MonadIO m+  => e `CouldBe` NotPositive+  => e `CouldBe` NotAnInteger+  => String+  -> ExceptT (Variant e) m Int+example9 s = do+  i <- liftIO (runExceptT (readInt s))+    & OO.onLeftThrow+  pos <- liftIO (runExceptT (requirePositive i))+    & OO.onLeftThrow+  liftIO $ IO.print pos+  return pos+  where+    readInt :: String -> ExceptT NotAnInteger IO Int+    readInt = error "unimplemented"+    requirePositive :: Int -> ExceptT NotPositive IO Int+    requirePositive = error "unimplemented"++-- We can similarly call a function that throws via vanilla 'ExceptT'+example10 :: ()+  => MonadIO m+  => e `CouldBe` NotPositive+  => e `CouldBe` NotAnInteger+  => String+  -> ExceptT (Variant e) m Int+example10 s = do+  i <- liftIO (runExceptT (readInt s))+    & OO.onLeftThrow+  pos <- liftIO (runExceptT (requirePositive i))+    & OO.onLeftThrow+  liftIO $ IO.print pos+  return pos+  where+    readInt :: String -> ExceptT NotAnInteger IO Int+    readInt = error "unimplemented"+    requirePositive :: Int -> ExceptT NotPositive IO Int+    requirePositive = error "unimplemented"++-- We can also catch runtime exceptions and rethrow them as checked exceptions.+example11 :: ()+  => MonadIO m+  => MonadCatch m+  => e `CouldBe` IOException+  => ExceptT (Variant e) m String+example11 = do+  i <- liftIO (IO.readFile "moo")+    & OO.onException @IOException OO.throw+  liftIO $ IO.print i+  return i++-- The 'onExceptionThrow' is shorthand for 'onException throw'.+example12 :: ()+  => MonadIO m+  => MonadCatch m+  => e `CouldBe` IOException+  => ExceptT (Variant e) m String+example12 = do+  i <- liftIO (IO.readFile "moo")+    & OO.onExceptionThrow @IOException+  liftIO $ IO.print i+  return i