In the process of writing an assignment for university I am having the ever-joyous fun of learning new Haskell monads. Yay!!!
I have a function that typechecks just fine:
compile :: Prog -> State VarsState String
compile prog@(Prog functions) = do
s1 <- sequence (map (translate_func 0) [get_function prog name | name <- [func_name func | func <- functions]])
return $ trace ("here's the program: \n" ++ show prog) $ concat $ s1
but when this other function:
maybe_compile_prog ::
MaybeOK Prog -> String -> IO ()
maybe_compile_prog (Error msg) _ = do
putStrLn ("error: " ++ msg)
maybe_compile_prog (OK prog) modulename = do
s1 <- compile prog
writeFile (modulename ++ ".m") ((header modulename) ++ s1)
tries to call it, it blows up at the line
s1 <- compile prog
saying it couldn't match the expected type "IO t0" with actual type "State VarsState String".
I assume this is because maybe_compile_prog returns type IO () so it expects to only unwrap IO information? VarsState is a custom datatype I have made to use with the State monad/
However, if that is the problem and I assume it is, I don't know how to transmit this simple string to maybe_compile_prog. Really, that's all I want to do - give a string to maybe_compile_prog.
Perhaps there's some neat way to unwrap this state monad? Perhaps it's possible to rewrite "compile" so that it takes in some state monad information whilst it runs, but then just returns a string (not wrapped in any monad)?
Please let me know if I'm missing any information.
The state monad is a built in monad in Haskell that allows for chaining of a state variable (which may be arbitrarily complex) through a series of function calls, to simulate stateful code. It is defined as: newtype State s a = State { runState :: (s -> (a,s)) }
In Haskell a monad is represented as a type constructor (call it m ), a function that builds values of that type ( a -> m a ), and a function that combines values of that type with computations that produce values of that type to produce a new computation for values of that type ( m a -> (a -> m b) -> m b ).
The Reader monad (also called the Environment monad). Represents a computation, which can read values from a shared environment, pass values from function to function, and execute sub-computations in a modified environment. Using Reader monad for such computations is often clearer and easier than using the State monad.
It transforms a monadic action of one monad to an action of a transformed monad. In general, "lift" lifts a function/action into a "wrapped" type (so the original function gets to work "under the wraps").
compile prog
is an action in the State VarsState
monad, thus you cannot use it in an IO
-do-block as such. In a do-block, all lines must use the same monad, in this case IO
.
You need to "run" the compile
action to obtain the result, with one of
runState :: State s a -> s -> (a,s)
evalState :: State s a -> s -> a
execState :: State s a -> s -> s
depending on whether you need
In your case, you only want the result, so evalState
it is.
For that you need to provide an initial state, it could look like
maybe_compile_prog (OK prog) modulename = do
let s1 = evalState (compile prog) initialState
writeFile (modulename ++ ".m") ((header modulename) ++ s1)
but then the provided initial state for the compile
action would be the same for all OK prog
s passed. If that is not the right thing, you could pass the initial state as a parameter too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With