monad-effect-0.1.0.0: test/Examples.hs
module Examples where
import Control.Exception
import Control.Monad
import Control.Monad.Effect -- the EffT types and useful combinators
import Module.RS -- built in modules, a reader module and a state module
import System.IO
import qualified Data.Map as M
import qualified Data.Text as T
-- $ our monad-effect provides **module management** and **composable exceptions**
-- it's algebraic, performant, make sense, without sacrificing purity
-- | Wraps your effectul routine into EffT monad transformer
myLookup :: (Show k, Ord k, Monad m) => k -> EffT '[SModule (M.Map k v)] '[ErrorText "Map.keyNotFound"] m v
myLookup k
= effMaybeInWith (errorText @"Map.keyNotFound" $ " where key = " <> T.show k) -- wraps Maybe into an exception
$ getsS (M.lookup k) -- this just returns a monadic value of type `Maybe v`
-- | This effect can run in pure monads! like Identity
lookups :: forall v m. (Monad m) => EffT '[SModule (M.Map T.Text v)] '[ErrorText "Map.keyNotFound"] m (v, v, v)
lookups = do
foo <- myLookup "foo" -- this will throw an exception if "foo" is not found
bar <- myLookup "bar" -- instead of Nothing, you get an algebraic exception `ErrorText "Map.keyNotFound"` explaining what went wrong
baz <- myLookup "baz" -- just like Maybe and Either, when an exception is thrown, the computation stops and immediately returns
return (foo, bar, baz)
parse :: String -> Maybe [Double]
parse = undefined
computeAverageFromFile
:: FilePath
-> Eff
'[SModule (M.Map T.Text Int)] -- this effect can read and modify a value of type (Map Text Int)
[ IOException -- composable and explicit exceptions
, ErrorText "empty-file" -- you know what types of error this effect can produce
, ErrorText "zero-numbers" -- just by observing its type signature
, ErrorText "Map.keyNotFound"
]
Double -- return type
computeAverageFromFile fp = do
-- | the `liftIOException :: IO a -> Eff '[] '[IOException] a` captures `IOException`
content <- embedError . liftIOException $ readFile' fp
-- | throw an Algebraic error instead of an exception that you have no idea
when (null content) $ do
effThrowIn ("file is empty" :: ErrorText "empty-file")
-- | this `pureMaybeInWith :: In e es => e -> Maybe a -> Eff mods es a` turns a Maybe value into an ad-hoc exception type!
parsed <- pureMaybeInWith ("parse error" :: ErrorText "parse-error") (parse content)
`effCatch` (\(_ :: ErrorText "parse-error") -> return [0])
-- ^ you can catch exception and deal with it, so the error is eliminated from the list
-- | The type system will check whether you have the module needed to perform this action
_ <- embedEffT $ lookups @Int
-- | The type system will force you remember that we can return an exception with an custom type `ErrorText "zero-numbers"`
when (null parsed) $ do
effThrowIn ("zero numbers" :: ErrorText "zero-numbers")
return $ sum parsed / fromIntegral (length parsed)