{-# LANGUAGE InstanceSigs #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- | Notes taken by Julija Mikeliūnaitė
module Lessons.Lesson09 where

import Lessons.Lesson08(Parser(..), threeLetters)

import Control.Applicative

import Control.Monad.Trans.State.Strict (State, StateT, get, put, runState, runStateT)
import Control.Monad.Trans.Except (ExceptT, throwE, runExceptT)
import Control.Monad.Trans.Class(lift)
import Control.Monad.IO.Class(liftIO)

import Control.Monad
import Data.Foldable

-- | Take a list as a traversable, pass a list of Maybies. Returns a Maybe containing a list.
--
-- >>> sequenceA [Just 42, Just 5]
-- Just [42,5]

-- | If the passed list contains a Nothing, Nothing is returned.
--
-- >>> sequenceA [Just 42, Just 5, Nothing]
-- Nothing

-- | Works pretty much as list comprehension.
--
-- >>> sequenceA [[42], [13, 45]]
-- [[42,13],[42,45]]

-- | Returns empty list.
--
-- >>> sequenceA [[42], [13, 45], []]
-- []

-- >>> sequenceA [Right 43, Right 45]
-- Right [43,45]

-- | Because a Left value was passed, it also returns a Left value.
--
-- >>> sequenceA [Left 0, Right 43, Right 45]
-- Left 0

-- >>> sequenceA $ Right (Right 45)
-- Right (Right 45)

-- | Returns the inner value (Left 45).
--
-- >>> sequenceA $ Right (Left 45)
-- Left 45

-- | Swaps the traversable with applicative.
--
-- >>> sequenceA $ Just (Right 45)
-- Right (Just 45)

-- | Collects all the inputs. Inner values are Just monadic values, type is IO [String]. Result is an IO list of pure values, which makes life easier.
--
-- >>> :t sequenceA [getLine, getLine, getLine]
-- sequenceA [getLine, getLine, getLine] :: IO [String]

-- >>> :t [getLine, getLine, getLine]
-- [getLine, getLine, getLine] :: [IO String]

-- | Takes pure value (name) and returns a monadic value.
--
queryAge :: String -> IO Integer
queryAge :: String -> IO Integer
queryAge String
name = do
    String -> IO ()
putStrLn (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$ String
"What is your age, " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"?"
    String -> Integer
forall a. Read a => String -> a
read (String -> Integer) -> IO String -> IO Integer
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO String
getLine

-- | If a list of names is provided, it returns a list of numbers (type IO [Integer]).
--
-- >>> :t mapM queryAge ["VI", "A", "U"]
-- mapM queryAge ["VI", "A", "U"] :: IO [Integer]

-- | Return type is [IO Integer] (not IO [Integer]).
--
-- >>> :t map queryAge ["VI", "A", "U"]
-- map queryAge ["VI", "A", "U"] :: [IO Integer]

-- | Usually functions with _'s drop the final result and only run side-effects.  Running a bunch of computations when you don't really care about the result.
--
-- >>> :t mapM_ queryAge ["VI", "A", "U"]
-- mapM_ queryAge ["VI", "A", "U"] :: IO ()

-- | Closest thing Haskell has to loop (similar to 'foreach' in other languages). Usually used for running a computation over some collection. Works the same way as mapM_ but has swapped arguments.
--
-- >>> :t forM_ ["VI", "A", "U"] queryAge
-- forM_ ["VI", "A", "U"] queryAge :: IO ()

-- | The number next to M shows how many arguments the first function takes. Works as (<$>).
--
-- >>> liftM2 (+) (Just 4) (Just 6)
-- Just 10

-- >>>  (+) <$> (Just 4) <*> (Just 6)
-- Just 10

-- | Lifts a pure function into a monadic function.
--
-- >>> liftM3 (\a b c -> a + b - c) (Just 4) (Just 6) Nothing
-- Nothing

-- | Same result as before.
--
-- >>> liftA3 (\a b c -> a + b - c) (Just 4) (Just 6) Nothing
-- Nothing

-- | Using our helpers in the context of parsers (making them useful).
--
-- >>> :t threeLetters
-- threeLetters :: Parser String

-- >>> :t sequenceA [threeLetters, threeLetters, threeLetters]
-- sequenceA [threeLetters, threeLetters, threeLetters] :: Parser [String]

-- >>> :t runParser (sequenceA [threeLetters, threeLetters, threeLetters])
-- runParser (sequenceA [threeLetters, threeLetters, threeLetters]) :: String -> Either String ([String], String)

-- | After the first parser completes its course, the second parser continues with the unparsed string.
--
-- >>> runParser (sequenceA [threeLetters, threeLetters, threeLetters]) "asdasdasd!"
-- Right (["asd","asd","asd"],"!")

-- | Pretty much every program is just a traversable.

-- | Allows to write parser combinators in a monadic way.
--
instance Monad Parser where
    (>>=) :: Parser a -> (a -> Parser b) -> Parser b
    Parser a
ma >>= :: forall a b. Parser a -> (a -> Parser b) -> Parser b
>>= a -> Parser b
mf = (String -> Either String (b, String)) -> Parser b
forall a. (String -> Either String (a, String)) -> Parser a
Parser ((String -> Either String (b, String)) -> Parser b)
-> (String -> Either String (b, String)) -> Parser b
forall a b. (a -> b) -> a -> b
$ \String
input ->
        case Parser a -> String -> Either String (a, String)
forall a. Parser a -> String -> Either String (a, String)
runParser Parser a
ma String
input of
            Left String
e1 -> String -> Either String (b, String)
forall a b. a -> Either a b
Left String
e1
            Right (a
a, String
r1) ->
                case Parser b -> String -> Either String (b, String)
forall a. Parser a -> String -> Either String (a, String)
runParser (a -> Parser b
mf a
a) String
r1 of
                    Left String
e2 -> String -> Either String (b, String)
forall a b. a -> Either a b
Left String
e2
                    Right (b
b, String
r2) -> (b, String) -> Either String (b, String)
forall a b. b -> Either a b
Right (b
b, String
r2)

-- >>> runParser threeThreeLetterWords "asdasdasd!"
-- Right (["asd","asd","asd"],"!")
threeThreeLetterWords:: Parser [String]
threeThreeLetterWords :: Parser [String]
threeThreeLetterWords = do
    String
a <- Parser String
threeLetters
    String
b <- Parser String
threeLetters
    String
c <- Parser String
threeLetters
    [String] -> Parser [String]
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure [String
a, String
b, String
c]

-- | State monad - it tries to pretend that Haskell is a normal programming language with a mutable state.
-- | State has two params: String - type of state, and Int - type of computation result. get - gets global state, put - changes global state. In this case, global is super local.
--
-- >>> runState stateful "initial"
-- (7,"I am a new state")
stateful :: State String Int
stateful :: State String Int
stateful = do
    String
value <- StateT String Identity String
forall (m :: * -> *) s. Monad m => StateT s m s
get
    String -> StateT String Identity ()
forall (m :: * -> *) s. Monad m => s -> StateT s m ()
put String
"I am a new state"
    Int -> State String Int
forall a. a -> StateT String Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> State String Int) -> Int -> State String Int
forall a b. (a -> b) -> a -> b
$ String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
value

-- | 7 is the old state length, 16 is the new one.
--
-- >>> runState combined "initial"
-- ((7,16),"I am a new state")
combined :: State String (Int, Int)
combined :: State String (Int, Int)
combined = do
    Int
a <- State String Int
stateful
    Int
b <- State String Int
stateful
    (Int, Int) -> State String (Int, Int)
forall a. a -> StateT String Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int
a, Int
b)

-- >>> runState combined' "initial"
-- ((7,16),"I am a new state")
combined' :: State String (Int, Int)
combined' :: State String (Int, Int)
combined' = (,) (Int -> Int -> (Int, Int))
-> State String Int -> StateT String Identity (Int -> (Int, Int))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>  State String Int
stateful StateT String Identity (Int -> (Int, Int))
-> State String Int -> State String (Int, Int)
forall a b.
StateT String Identity (a -> b)
-> StateT String Identity a -> StateT String Identity b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> State String Int
stateful

-- >>> runState combined'' "initial"
-- ((7,16),"I am a new state")
combined'' :: State String (Int, Int)
combined'' :: State String (Int, Int)
combined'' = (Int -> Int -> (Int, Int))
-> State String Int -> State String Int -> State String (Int, Int)
forall a b c.
(a -> b -> c)
-> StateT String Identity a
-> StateT String Identity b
-> StateT String Identity c
forall (f :: * -> *) a b c.
Applicative f =>
(a -> b -> c) -> f a -> f b -> f c
liftA2 (,) State String Int
stateful State String Int
stateful