This commit is contained in:
2025-03-21 17:09:04 +01:00
parent 923a17f7ff
commit ed3e43889e
11 changed files with 278 additions and 0 deletions

BIN
haskell/scratch/Monadic Executable file

Binary file not shown.

BIN
haskell/scratch/Monadic.hi Normal file

Binary file not shown.

View File

@ -0,0 +1,77 @@
-- 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?

BIN
haskell/scratch/Monadic.o Normal file

Binary file not shown.

View File

@ -0,0 +1,66 @@
-- 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