<jupyter_start><jupyter_text>Chapter 23: State<jupyter_code>:!stack install random
-- maybe its not needed actually, should be part of standard installation
import System.Random
:t mkStdGen
:info StdGen
:info RandomGen
:t next
:info Random
:t random
mkStdGen 0
mkStdGen 10
sg = mkStdGen 0
next sg
random sg :: (Float, StdGen)
import Data.Maybe

data Die
    = DieOne
    | DieTwo
    | DieThree
    | DieFour
    | DieFive
    | DieSix
    deriving (Eq, Show)

intToDie :: Int -> Maybe Die
intToDie 1 = Just DieOne
intToDie 2 = Just DieTwo  
intToDie 3 = Just DieThree
intToDie 4 = Just DieFour
intToDie 5 = Just DieFive 
intToDie 6 = Just DieSix
intToDie _ = Nothing

unsafeIntToDie :: Int -> Die
unsafeIntToDie = fromJust . intToDie

dieToInt :: Die -> Int
dieToInt DieOne = 1
dieToInt DieTwo = 2
dieToInt DieThree = 3
dieToInt DieFour = 4
dieToInt DieFive = 5
dieToInt DieSix = 6
:info Functor
:info Foldable
:info Traversable
import Control.Applicative (liftA3)

data Tuple3Homo a = Tuple3Homo a a a

instance Functor Tuple3Homo where
    fmap f (Tuple3Homo a b c) = Tuple3Homo (f a) (f b) (f c)
    
instance Foldable Tuple3Homo where
    foldMap f (Tuple3Homo a b c) = f a <> f b <> f c
    
instance Traversable Tuple3Homo where
    traverse f (Tuple3Homo a b c) = liftA3 Tuple3Homo (f a) (f b) (f c)
    
toTuple :: Tuple3Homo a -> (a, a, a)
toTuple (Tuple3Homo a b c) = (a, b, c)
rollDieThreeTimes' :: RandomGen g => g -> (Die, Die, Die)
rollDieThreeTimes' g = toTuple . fromJust . traverse intToDie $ Tuple3Homo d1 d2 d3 where
    r = randomR (1, 6)
    (d1, g1) = r g
    (d2, g2) = r g1
    (d3, _) = r g2
rollDieThreeTimes :: (Die, Die, Die)
rollDieThreeTimes = rollDieThreeTimes' (mkStdGen 0)
rollDieThreeTimes
import Control.Monad (replicateM)
import Control.Monad.Trans.State
:t replicateM
:info State
:info StateT
:t state
:t state (randomR (1, 6))
:t intToDie <$> state (randomR (1, 6))
rollDie :: State StdGen Die
rollDie = unsafeIntToDie <$> state (randomR (1, 6))
:t rollDie
rollDieThreeTimes' :: State StdGen (Die, Die, Die)
rollDieThreeTimes' = liftA3 (,,) rollDie rollDie rollDie
:t evalState
:t runState
evalState rollDieThreeTimes' (mkStdGen 0)
:t repeat
infiniteDie :: State StdGen [Die]
infiniteDie = repeat <$> rollDie
take 6 $ evalState infiniteDie (mkStdGen 0)
import Control.Applicative (liftA2)

replicateM' :: Applicative m => Int -> m a -> m [a]
replicateM' cnt0 f = loop cnt0 where
    loop cnt
        | cnt <= 0  = pure []
        | otherwise = liftA2 (:) f (loop (cnt - 1))

repeatM :: Applicative m => m a -> m [a]
repeatM f = loop where
    loop = liftA2 (:) f loop
evalState (replicateM' 6 rollDie) (mkStdGen 0)
take 6 $ evalState (repeatM rollDie) (mkStdGen 0)
infiniteDie' :: State StdGen [Die]
infiniteDie' = repeatM rollDie
take 6 $ evalState infiniteDie' (mkStdGen 0)
nDie :: Int -> State StdGen [Die]
nDie n = replicateM' n rollDie
evalState (nDie 6) (mkStdGen 0)
:t scanl
:t state
:t runState
:t repeatM (flip runState <$> rollDie)
rollsToGetN :: Int -> State StdGen (Int, [Die])
rollsToGetN n = f <$> repeatM rollDie where
    f rolls = (l, origs) where
        ints = dieToInt <$> rolls
        scans = drop 1 . scanl (+) 0 $ ints
        filtered = takeWhile (<n) scans
        l = length filtered
        origs = take l rolls
evalState (rollsToGetN 30) (mkStdGen 0)<jupyter_output><empty_output><jupyter_text>---<jupyter_code>newtype Moi s a = Moi { runMoi :: s -> (a, s) }
instance Functor (Moi s) where
    fmap f (Moi g) = Moi h where
        h s = let (a, s') = g s in (f a, s')
runMoi ((+1) <$> (Moi $ \s -> (0, s))) 0
instance Applicative (Moi s) where
    pure a = Moi $ \s -> (a, s)
    
    (Moi ff) <*> (Moi g) = Moi h where
        h s = (f a, s'') where
            (f, s') = ff s
            (a, s'') = g s'
    -- order of state application is defined by law:
    -- pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
:info Monad
instance Monad (Moi s) where
    (Moi f) >>= mg = Moi h where
        h s = g s' where
            (a, s') = f s
            Moi g = mg a<jupyter_output><empty_output><jupyter_text>---<jupyter_code>get' :: Moi s s
get' = Moi $ \s -> (s, s)

runMoi get' "curry is Amaze"
put' :: s -> Moi s ()
put' s = (Moi . const) ((), s)

runMoi (put' "blah") "woot"
exec' :: Moi s a -> s -> s
exec' (Moi sa) = snd . sa

exec' (put' "wilma") "daphne"
eval' :: Moi s a -> s -> a
eval' (Moi sa) = fst . sa

eval' (put' "wilma") "daphne"
modify' :: (s -> s) -> Moi s ()
modify' f = Moi $ \s -> ((), f s)

runMoi (modify' (+1)) 0<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13153829176782167, 'fraction_numerical': 0.017876848377841537, 'mean_word_length': 3.1050724637681157, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '417678', 'license': 'MIT', 'n_tokens_mistral': 1849, 'n_tokens_neox': 1770, 'n_words': 625, 'path': 'src/haskell-programming-from-first-principles/23-state.ipynb', 'repo_name': 'Eoksni/ihaskell-playground', 'size': '4531'}