-- 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) instance Functor MyMonad where fmap f (DivByZeroError s) = DivByZeroError s fmap f (Divisible a) = Divisible (f a) fmap f (NotDivisible a) = NotDivisible a -- This is where we define <*> (i.e. apply but for monads) essentially applying the -- "wrapped" function to the "wrapped" value instance Applicative MyMonad where (DivByZeroError s) <*> _ = DivByZeroError s (Divisible a) <*> b = fmap a b (NotDivisible a) <*> b = NotDivisible a pure = Divisible -- This is where we define >>=, the bind operator, which chains monadic operations -- i.e. in this case, we chain the operations of dividing and checking if the number is divisible 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 -- Process the divisors (map out the results of tryDivide) sequentialDividing :: [Int] -> Int -> MyMonad Int sequentialDividing [] n = return n sequentialDividing (d:ds) n = do newN <- tryDivide d n 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?