-- | Notes taken by Emilija Rimšelytė
module Lessons.Lesson07 where

-- | List comprehension: pairing numbers with letters.
-- For each 'a' and 'b', we build a tuple.
-- A cartesian product, clean and subtle.
--
-- >>> lc 
-- [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b'),(4,'a'),(4,'b')]
lc :: [(Integer, Char)]
lc :: [(Integer, Char)]
lc = [(Integer
a, Char
b) | Integer
a <- [Integer
1,Integer
2,Integer
3,Integer
4], Char
b <- [Char
'a', Char
'b']]

-- | Squares of 'a', repeated for each 'b'.
-- 'b' isn’t used, but it multiplies the count.
-- More combinations — that’s the amount.
--
-- >>> lc'
-- [1,1,4,4,9,9]
lc' :: [Integer]
lc' :: [Integer]
lc' = [Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
a | Integer
a <- [Integer
1,Integer
2,Integer
3], Integer
b <- [Integer
1,Integer
2]]

-- | 'b' repeats for each 'c', though 'c' is unused.
-- 'a' is fixed, but nesting boosts the size.
--
-- >>> lc''
-- [1,1,2,2,3,3,4,4]
lc'' :: [Integer]
lc'' :: [Integer]
lc'' = [Integer
b | Integer
a <- [Integer
1], Integer
b <- [Integer
1,Integer
2,Integer
3,Integer
4], Char
c <- [Char
'a', Char
'z']]

-- | Empty list in 'b' means no results.
-- Comprehension short-circuits — nothing to compute.
--
-- >>> lc'''
-- []
lc''' :: [Integer]
lc''' :: [Integer]
lc''' = [Integer
a | Integer
a <- [Integer
1,Integer
2,Integer
3], Any
b <- []]

-- | Same idea, but now 'a' is empty.
-- No values to pair, so the list stays bare.
--
-- >>> lc''''
-- []
lc'''' :: [Integer]
lc'''' :: [Integer]
lc'''' = [Integer
a | Integer
a <- [], Integer
b <- [Integer
1,Integer
2,Integer
3]]

-- | Do-notation version of 'lc'.
-- Same result, just written differently.
-- Shows how monads can express list logic.
--
-- >>> lm
-- [(1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b'),(4,'a'),(4,'b')]
lm :: [(Integer, Char)]
lm :: [(Integer, Char)]
lm = do
    Integer
a <- [Integer
1,Integer
2,Integer
3,Integer
4]
    Char
b <- [Char
'a', Char
'b']
    (Integer, Char) -> [(Integer, Char)]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a, Char
b)

-- | Swapping the order changes the output.
-- First 'b', then 'a' — so the nesting flips.
--
-- >>> lm'
-- [(1,'a'),(2,'a'),(3,'a'),(4,'a'),(1,'b'),(2,'b'),(3,'b'),(4,'b')]
lm' :: [(Integer, Char)]
lm' :: [(Integer, Char)]
lm' = do
    Char
b <- [Char
'a', Char
'b']
    Integer
a <- [Integer
1,Integer
2,Integer
3,Integer
4]
    (Integer, Char) -> [(Integer, Char)]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a, Char
b)

-- | Maybe monad: chaining computations.
-- All values must be 'Just', or the chain breaks.
-- One 'Nothing', and the whole thing shakes.
--
-- >>> mm
-- Just 84
mm :: Maybe Integer
mm :: Maybe Integer
mm = do
    Integer
a <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
42
    Integer
b <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
2
    Integer -> Maybe Integer
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Maybe Integer) -> Integer -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b

-- | One 'Nothing' in the chain — result is 'Nothing'.
-- That’s how Maybe guards against failure.
--
-- >>> mm'
-- Nothing
mm' :: Maybe Integer
mm' :: Maybe Integer
mm' = do
    Integer
a <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
42
    Integer
b <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
2
    Any
c <- Maybe Any
forall a. Maybe a
Nothing
    Integer -> Maybe Integer
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Maybe Integer) -> Integer -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b

-- | Looks like it should work, but it doesn’t.
-- You need to bind the final value too.
-- Just expression alone won’t pull it through.
--
-- >>> mm''
-- Nothing
mm'' :: Maybe Integer
mm'' :: Maybe Integer
mm'' = do
    Integer
a <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
42
    Integer
b <- Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
2
    Integer -> Maybe Integer
forall a. a -> Maybe a
Just (Integer -> Maybe Integer) -> Integer -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b

-- | Either monad: 'Right' flows like Maybe’s 'Just'.
-- 'Left' short-circuits — it’s an error path.
--
-- >>> em
-- Right 'a'
em :: Either String Char
em :: Either String Char
em = do
    Integer
a <- Integer -> Either String Integer
forall a b. b -> Either a b
Right Integer
43
    Char
b <- Char -> Either String Char
forall a b. b -> Either a b
Right Char
'a'
    Char -> Either String Char
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
b

-- | 'Left' stops the computation early.
-- Doesn’t matter what comes after — it’s done.
--
-- >>> em'
-- Left "oh"
em' :: Either String Char
em' :: Either String Char
em' = do
    Any
a <- String -> Either String Any
forall a b. a -> Either a b
Left String
"oh"
    Char
b <- Char -> Either String Char
forall a b. b -> Either a b
Right Char
'a'
    Char -> Either String Char
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
b

-- | You can return tuples too — Either handles it.
-- As long as all are 'Right', it’s alright.
--
-- >>> em''
-- Right (43,'a')
em'' :: Either String (Integer, Char)
em'' :: Either String (Integer, Char)
em'' = do
    Integer
a <- Integer -> Either String Integer
forall a b. b -> Either a b
Right Integer
43
    Char
b <- Char -> Either String Char
forall a b. b -> Either a b
Right Char
'a'
    (Integer, Char) -> Either String (Integer, Char)
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a, Char
b)

-- | Mixing 'Left' and 'Right' — 'Left' wins.
-- Error takes over, no multiplication begins.
--
-- >>> em'''
-- Left 42
em''' :: Either Integer Integer
em''' :: Either Integer Integer
em''' = do
    Integer
a <- Integer -> Either Integer Integer
forall a b. b -> Either a b
Right Integer
43
    Integer
b <- Integer -> Either Integer Integer
forall a b. a -> Either a b
Left Integer
42
    Integer -> Either Integer Integer
forall a. a -> Either Integer a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Either Integer Integer)
-> Integer -> Either Integer Integer
forall a b. (a -> b) -> a -> b
$ Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b

-- lm = do
--     a <- [1,2,3,4]
--     b <- ['a', 'b']
--     return (a, b)

-- mm = do
--     a <- Just 42
--     b <- Just 2
--     return $ a * b

-- | Same as 'lm', but using '>>=' explicitly.
-- Do-notation is just syntactic sugar.
-- Shows the plumbing under the rug.
lmb :: [(Integer, Char)]
lmb :: [(Integer, Char)]
lmb = [Integer
1,Integer
2,Integer
3,Integer
4] [Integer] -> (Integer -> [(Integer, Char)]) -> [(Integer, Char)]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Integer
a -> [Char
'a', Char
'b'] String -> (Char -> [(Integer, Char)]) -> [(Integer, Char)]
forall a b. [a] -> (a -> [b]) -> [b]
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Char
b -> (Integer, Char) -> [(Integer, Char)]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
a, Char
b)))

-- | Same as 'mm', but written with '>>='.
-- Shows how monads chain computations.
--
-- >>> mmb
-- Just 84
mmb :: Maybe Integer
mmb :: Maybe Integer
mmb = Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
42 Maybe Integer -> (Integer -> Maybe Integer) -> Maybe Integer
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Integer
a -> Integer -> Maybe Integer
forall a. a -> Maybe a
Just Integer
2 Maybe Integer -> (Integer -> Maybe Integer) -> Maybe Integer
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Integer
b -> Integer -> Maybe Integer
forall a. a -> Maybe a
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Maybe Integer) -> Integer -> Maybe Integer
forall a b. (a -> b) -> a -> b
$ Integer
a Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
b))

-- em = do
--     a <- Right 43
--     b <- Right 'a'
--     return b

-- | Same as 'em', but with explicit binds.
-- Monads let you sequence effects — even errors.
emb :: Either String Char
emb :: Either String Char
emb = Integer -> Either String Integer
forall a b. b -> Either a b
Right Integer
43 Either String Integer
-> (Integer -> Either String Char) -> Either String Char
forall a b.
Either String a -> (a -> Either String b) -> Either String b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Integer
a -> Char -> Either String Char
forall a b. b -> Either a b
Right Char
'a' Either String Char
-> (Char -> Either String Char) -> Either String Char
forall a b.
Either String a -> (a -> Either String b) -> Either String b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= (\Char
b -> Char -> Either String Char
forall a. a -> Either String a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
b))