-- We want to take a list of divisors and a number -- We traverse the list of divisors and check if the number is divisible by the divisor -- If the number is divisible by the return the number divided by the divisor -- If the number is not divisible repeatedly increment the number by 1 until it is divisible by the divisor -- If the divisor is 0, throw an error data MyMonad a = DivByZeroError String | NotDivisible Int | Divisible a deriving (Show) -- This is where we define fmap (i.e. map but for monads) -- Essentially, this is where we define what happens when we apply a function to a monad -- i.e.: -- fmap (+1) (Divisible 3) => Divisible 4 -- fmap (+1) (NotDivisible 3) => NotDivisible 3 -- fmap (+1) (DivByZeroError "error") => DivByZeroError "error" instance Functor MyMonad where fmap f (DivByZeroError s) = DivByZeroError s -- Note the error fmap f (Divisible a) = Divisible (f a) -- Apply the function f to the value a fmap f (NotDivisible a) = NotDivisible a -- Not divisible, propagate the value forward -- This is where we define <*> (i.e. apply but for monads) essentially applying the -- "wrapped" function to the "wrapped" value -- i.e.: -- (Divisible (+1)) <*> (Divisible 3) => Divisible 4 -- (Divisible (+1)) <*> (NotDivisible 3) => NotDivisible 3 -- (Divisible (+1)) <*> (DivByZeroError "error") => DivByZeroError "error" instance Applicative MyMonad where (DivByZeroError s) <*> _ = DivByZeroError s -- Note the error (Divisible a) <*> b = fmap a b -- Apply the function a to the value b (NotDivisible a) <*> b = NotDivisible a -- Not divisible, propagate the value forward pure = Divisible -- Wrap the value in the Divisible constructor -- This is where we define >>=, the bind operator, which chains monadic operations -- i.e. in this case, we chain: -- tryDivide 2 420 >>= tryDivide 3 >>= tryDivide 5 >>= tryDivide 9 >>= tryDivide 0 -- If we encounter a DivByZeroError, we propagate it forward (i.e. we don't do anything) -- If we encounter a NotDivisible, we propagate it forward -- If we encounter a Divisible, we apply the function f to the value x instance Monad MyMonad where DivByZeroError msg >>= _ = DivByZeroError msg -- if we encounter a DivByZeroError, we propagate it forward NotDivisible n >>= _ = NotDivisible n -- if we encounter a NotDivisible, we propagate it forward Divisible x >>= f = f x -- if we encounter a Divisible, we apply the function f to the value x -- Try to divide the number n by d tryDivide :: Int -> Int -> MyMonad Int tryDivide 0 _ = DivByZeroError "Division by zero" tryDivide d n | n `mod` d == 0 = Divisible (n `div` d) | otherwise = NotDivisible n -- Sequentially divide the number n by the divisors in the list sequentialDividing :: [Int] -> Int -> MyMonad Int sequentialDividing [] n = return n sequentialDividing (d:ds) n = do newN <- tryDivide d n -- Note how we're not doing any pattern matching here, we're just applying the function, and the monad takes care of the rest sequentialDividing ds newN main :: IO () main = do let divisors = [2, 3, 5, 9, 0] let number = 420 -- 420/2 = 210 -- 210/3 = 70 -- 70/5 = 14 -- 14/9 not good => 14 is returned print $ sequentialDividing divisors number let divisors2 = [2, 3, 5, 7, 0] let number2 = 420 -- 420/2 = 210 -- 210/3 = 70 -- 70/5 = 14 -- 14/7 = 2 -- 2/0 not good => error print $ sequentialDividing divisors2 number2 let divisors3 = [2, 3, 5, 7, 2] let number3 = 420 -- 420/2 = 210 -- 210/3 = 70 -- 70/5 = 14 -- 14/7 = 2 -- 2/2 = 1, all good => 1 is returned print $ sequentialDividing divisors3 number3 -- [Divisible 210,Divisible 140,Divisible 84,NotDivisible 420,DivByZeroError "Division by zero"] -- 420 is divisible by: [2,3,5] -- Wtf is the point of a monad here?