--

-- Name:
-- Email:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
module MonadInterpWorksheet where

data Store s x = St(s -> (x,s))

type Map = [(String,Value)]

-- Assumption that the list is a set
-- Order doesn't matter, and no duplicates

--------------------------------------
-- Find a value in the Map

find :: Eq a => a -> [(a,b)] -> b
find nm pairs = head [ v | (n,v) <- pairs, n==nm]

-- Update a value in a Map

update :: Eq a => a -> b -> [(a,b)] -> [(a,b)]
update nm value pairs = (nm,value) : [ (n,v) | (n,v) <- pairs, n /= nm ]

-----------------------------------------------
-- Here is a new language with variables and assignments

data T5 = Add5 T5 T5
        | Sub5 T5 T5
        | Mult5 T5 T5
        | Int5 Int
        | Var5 String
        | Assign5 String  T5


-- Question #1. Write an evaluator for the language T5
-- write it in functional style (that is don't use monads)
-- I started it for you.

type Value = Int

eval5a :: T5 -> Store Map Value
eval5a (Add5 x y) =
  St(\s-> let St f = eval5a x
              St g = eval5a y
              (x',s1) = f s
              (y',s2) = g s1
          in(x'+y',s2))
eval5a (Sub5 x y) = undefined
eval5a (Mult5 x y) = undefined
eval5a (Int5 n) = undefined
eval5a (Var5 s) = undefined
eval5a (Assign5 nm x) = undefined

-----------------------------------------------
-- Question #2 Now write the operators on the Store monad


-- Given a name "x", "getStore x" returns a
-- Store computation, that when run returns
-- the value associated in the Map
-- with the name given as input.

getStore :: String -> (Store Map Value)
getStore nm = St h
  where h s = undefined

-- Given a name "x", and a new value "v"
-- "putStore x v" returns a Store computation,
-- that when it runs returns unit, but updates
-- the map so "x" is now mapped to "v"

putStore :: String -> Value -> (Store Map ())
putStore nm n = undefined

-------------------------------------------------------
-- Question #3. Write an evaluator for the language T5 but
-- this time use use monads and the "do" syntax
-- Hint. use the operators on the Store Monad
-- I started it for you.

eval5 :: T5 -> Store Map Value
eval5 (Add5 x y) =
      do {x' <- eval5 x; y' <- eval5 y; return (x' + y')}
eval5 (Sub5 x y) = undefined
eval5 (Mult5 x y) = undefined
eval5 (Int5 n) = undefined
eval5 (Var5 s) = undefined
eval5 (Assign5 s x) = undefined


------------------------------
instance Monad (Store s) where
  return x = St(\ s -> (x,s))
  (>>=) (St f) g = St h
    where h s1 = g' s2 where (x,s2) = f s1
                             St g' = g x

--------------------------------------------
-- Question #4

type Store2 a = a  -- replace a with monad transformer

getStore2 :: String -> Store2 Value
getStore2

putStore2 :: String -> Value -> Store2 ()
putStore2 = undefined


--------------------------------------------
-- Question #5


eval5T :: T5 -> Store2 Value
eval5T = undefined

--