78 lines
2.7 KiB
Haskell
78 lines
2.7 KiB
Haskell
-- 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?
|