
Hello all, I'm currently working on a (toy) ID3 [1] tag reader, which made me think of a library which might be quite useful. The structure of an ID3 tag goes something like this: Header: - total size of tag - other header info A series of frames, each with: - total size of frame - other header info - frame data Since the ID3 tag as a whole has size information, I need to pass that into the frame-reading functions to ensure that I never read past the end of the total header. This exact same requirement is present within the frame-reading function itself; it must read the frame data without reading past the end of the frame. It would be nice to have some kind of monad or similar so that I could do:
readTag = do size <- -- read size -- read other header info withLimit size $ -- read frames
then the read-frames function would be able to do its work without having to worry about size-checking all the time, as this would be implicit. (And similarly with the frame-reading functions as well.) This could easily be extended to:
f x = readManyWithLimit size readerFunction
where readManyWithLimit would return a list of results of applying the reader function as many times as possible within the size limit (and possibly returning some kind of error if the reader function 'bails early' and doesn't gobble right up to the end of the size limit). The thing is, I've not implemented a monad of any substantial size so I don't have a clue where to start. Calling it FileReader (for want of a better name), I guess it would almost be the same as a parsing monad, so the type would have a reference to the file Handle and the current index into the file, as well as the last point that it's allowed to read up to. So, umm... a rather open-ended question, but how would I go about this? [1]: http://www.id3.org/Developer_Information

This is something of the blind leading the blind, but you seem to need a bit of state monad mixed with IO monad. So you could look into StateT, a monad transformer, which should make you a StateIO monad specialized to FileReader. Then you define a `runFileReader` that allows you to jump into the FileReader monad. I defined a "pointer walking" monad a while back, but that did all the IO with `unsafePerformIO`, so I could use a pure State monad. -- _jsn

2008/10/1 George Pollard
Since the ID3 tag as a whole has size information, I need to pass that into the frame-reading functions to ensure that I never read past the end of the total header.
What you want for this is the environment monad, also known as the Reader monad in Haskell. It gives you something similar to a piece of read-only global state. For this application, you'd pass in two pieces of state: the file handle to read, and the limit on the number of bytes that a client can read. Since we can't easily combine the normal Reader monad and IO, we use a monad transformer stack. I've attached an example for your amusement.

On Thu, 2 Oct 2008, George Pollard wrote:
Hello all,
I'm currently working on a (toy) ID3 [1] tag reader, which made me think of a library which might be quite useful.
The structure of an ID3 tag goes something like this:
Header: - total size of tag - other header info A series of frames, each with: - total size of frame - other header info - frame data
Since the ID3 tag as a whole has size information, I need to pass that into the frame-reading functions to ensure that I never read past the end of the total header. This exact same requirement is present within the frame-reading function itself; it must read the frame data without reading past the end of the frame.
I've done exactly this for a MIDI file parser. It uses a State monad transformer which handles the number of remaining bytes in the chunk. http://darcs.haskell.org/midi/src/Sound/MIDI/Parser/Restricted.hs
participants (4)
-
Bryan O'Sullivan
-
George Pollard
-
Henning Thielemann
-
Jason Dusek