简体   繁体   中英

State Monads in Haskell

I am writing a function in Haskell that takes in a Java class file, and writes another class file that is identical but contains some modifications. For this, I feel that I definitely need a state monad to at least hold the [Word8] that holds all of the bytes of the class file. However, after all of my research on State Monads in Haskell, I am still having trouble figuring out how to do this. Can anyone point me in the right direction? I would like to be able to have a [Word8] (or you know, any data type) that is in scope for all of the functions and that I can modify from functions. I understand this involves using something like state<-get ... put newstate

but I really don't know where to start with defining the monad and whatnot.

Thanks so much in advance!

I'm not sure you do want a State monad. Depending on what kind of modifications you want to make, you are allowed to just pass the data you want to modify to every function that wants to modify it. State is usually for situations where you are producing a value in addition to modifying state, ie when you are writing a lot of functions that look like s -> (s,a) .

Try just a normal-function approach first. State isn't magic, it just makes certain types of code easier to write quickly, concisely, and correctly. Everything you can do with it you can also do without it, it's just a bit more tedious.

What you probably want, instead of the State Monad, is the ST monad and mutable vectors.

Use the IO monad to read the bytes in from the class file.

bytes <- readFile myClassFile

use runST to run your ST monad calculation on the given bytes:

let result = runST $ transform bytes

The ST monad gives you access to mutable vectors , which are a lot like C or Java arrays. They're indexed by integers, and have O(1) lookup and modify.

transform :: [Char] -> ST s [Char]
transform bytes = do
   mvec <- thaw $ fromList bytes
   -- you can read a value at an index
   val <- read mvec 0
   -- and set a value at an index
   write mvec 0 (val `xor` 0xff)
   -- ...
   -- turn it back into a list of bytes
   vec <- freeze mvec
   return $ toList vec

So just pass around the mvec to all your functions (which must return a ST action), and you'll be able to do whatever you want to the bytes.

If you don't want to bother with passing it as an argument, consider using the ReaderT monad transform to make the mvec implicitily available to all your code.

transform bytes = do
   -- ...
   runReaderT other mvec
   --- ... 

other :: ReaderT (MVector s Char) (ST s) String 
other = do
   -- ...
   -- grab the mvec when you need it
   mvec <- ask
   val <- lift $ read mvec 77
   lift $ write mvec 77 (val * 363 - 28)
   -- ...
   return "Hi!"

Of course, this is all assuming you need random access to the bytes. If you don't... then you probably don't need an MVector .

For example, if all you need to do is replace every instance of 0xDEADBEEF with 0xCAFEBABE , you could just use lists, no ST monad required:

 let newBytes = intsToBytes . map (\i -> if i == 0xDEADBEEF then 0xCAFEBABE else i) $ bytesToInts bytes

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM