
At Wed, 27 Jun 2007 18:54:58 +0200 (CEST), Arie Peterson wrote:
What is the status of the MIME Strike Force?
The goals proposed at http://www.haskell.org/haskellwiki/Libraries_and_tools/MIMEStrikeForce promise a very useful library. Has the design been initiated?
Currently it is on hold while I work on some other higher priority projects. But, I do hope to get back to it soon. (Or, perhaps someone else will have time to work on it). It seems to me that the mime-string package is very close to what we need for parsing MIME messages. If all you care about is parsing MIME messages, I highly recommend it. This leaves the problem of creating and transforming MIME messages. The real difficulty in this area is creating a good API. If you want to take a peek at what I have, take a look at: http://www.n-heptane.com/nhlab/repos/haskell-mime/APIs.hs Currently I am difficulty dealing with headers that could appear more than once. For example, the 'Keywords' header can appear multiple times. So, there are two cases to handle: 1. add an additional Keywords header 2. deleted any existing Keywords headers and add the new one Other fields, such as 'Subject', can appear only once. One way to express a filter that modifies an existing message is the following:
exampleHeaders = ( (setHeader (Subject "whee")) . (setHeader (Subject "bork")). (addHeader (Keywords ["baz", "bar", "bam"])) . (addHeader (Keywords ["zip", "zap", "zop"])) )
where setHeader ensures that a header only appears once, and addHeader appends the header, leaving existing instances alone. The type system ensures that you can never call addHeader on (Subject "whee"). Unfortunately, that code seems a bit verbose. we can make some helper functions that reduce the verbosity a bit,
subject = setHeader . Subject keywords = addHeader . Keywords
exampleHeaders3 :: [RawHeader] -> [RawHeader] exampleHeaders3 = ((subject "whee") . (subject "bork") . (keywords ["baz", "bar", "bam"]) . (keywords ["zip", "zap", "zop"]))
That is nice, except that we don't know which headers are going to use setHeader and which are doing to use addHeader. So, the results might be a bit suprising. Additionally, keywords always uses addHeader, but in some cases we might want it to use setHeader. Another option is to trying to use infix operators, such as: (.+.) for setHeader (.*.) for addHeader
exampleHeaders2 :: [RawHeader] exampleHeaders2 = ((Subject "whee") .+. (Subject "bork") .+. (Keywords ["baz", "bar", "bam"]) .*. (Keywords ["zip", "zap", "zop"]) .*. empty )
This is good because: 1. is it shorter than the first example that used setHeader/addHeader 2. the information about whether setHeader/addHeader is being used is preserved. 3. it is easy to choose which one you want (setHeader/addHeader) But, it is also really wonky because the operator has a bit of a postfix feel to it. For example, it is the .*. at the end of this line that is making it use addHeader.
(Keywords ["baz", "bar", "bam"]) .*.
If we wanted to use setHeader here, we would have to change it to:
(Keywords ["baz", "bar", "bam"]) .+.
This behaviour seems pretty unintutive. A whole other area I have not dealt with yet is data-mining and filters that depend on the values of existing fields. For example: 1. find all the headers that contain the string XXX 2. find all the Keywords fields and merge them into a single Keywords field So, that is where I currently am. Once the API is worked out, I think things should progress pretty easily. If you have any ideas, let me know. I think I may be picking this up again in September or October. HaXml, SYB, Uniplate, and HList seem like good places to get some ideas. If anyone has suggestions for other projects to look at, let me know. j.