-- error handling with a custom monad -- say we want to divide a list of numbers by a divisor -- if the divisor is even, we throw an error, then apply ^2 to the rest of the numbers -- if the divisor is odd, we return the result -- if the divisor is 0, we throw an error -- custom monad data MyMonad a = DivByZeroError String | DivByEven a | DivByOdd a deriving (Show) -- instance of Functor instance Functor MyMonad where -- Functor is where we define fmap (i.e. map) -- customMap, where we have 2 functions, one for even and one for odd fmap f (DivByZeroError s) = DivByZeroError s fmap f (DivByEven a) = DivByEven (f a) fmap f (DivByOdd a) = DivByOdd (f a) -- instance of Applicative instance Applicative MyMonad where (DivByZeroError s) <*> _ = DivByZeroError s -- DivByEvenError -> ^2 (DivByEven a) <*> b = fmap a b -- (DivByOdd a) <*> b = fmap a b pure = DivByOdd -- instance of Monad instance Monad MyMonad where return = pure (DivByZeroError s) >>= _ = DivByZeroError s (DivByEven a) >>= f = case f a of DivByOdd b -> DivByEven b -- propagate the "evenness context" forward DivByEven b -> DivByEven b -- keep even state if it continues DivByZeroError s -> DivByZeroError s (DivByOdd a) >>= f = f a safeDivide :: Int -> [Int] -> MyMonad [Int] safeDivide 0 _ = DivByZeroError "Division by zero" safeDivide d xs | even d = DivByEven (map (^2) xs) | otherwise = DivByOdd (map (`div` d) xs) main :: IO () main = do let nums = [69, 420, 666, 1337] let divisor = 2 let result = safeDivide divisor nums let nums2 = [1,2,3,4,5] let divisor2 = 0 let result2 = safeDivide divisor2 nums2 let nums3 = [69, 420, 666, 1337] let divisor3 = 3 let result3 = safeDivide divisor3 nums3 print result print result2 print result3