
Paul Moore wrote:
I'm thinking around the design of a couple of things, and am hitting an issue which I know how I would solve in Python, but I'm not sure what a good idiomatic Haskell approach would be.
The problem is that I am trying to write a function which takes a rather large number of arguments, many of which are optional (ie, have sensible defaults).
There's an interesting solution to this in section 15 (starting page 53) of Magnus Carlsson and Thomas Hallgren's Fudgets thesis available online at: http://www.cs.chalmers.se/~hallgren/Thesis/ (Fudgets main page is at http://www.cs.chalmers.se/ComputingScience/Research/Functional/Fudgets/ ). Basically the idea is that for any function which takes lots of params, the params are gathered together into a datatype whose details are hidden from the user of the function. For each parameter, the user is given a function called a "Customiser" in the thesis, which takes a value of the hidden datatype and modifies it by setting the relevant parameter. Multiple params can therefore be specified by chaining Customisers together in any order with the usual function composition. The original function, instead of taking lots of params, now just takes a single parameter: a Customiser, which is used to turn the default params into the customised params. This is similar to the idea of using records but has the advantage that by judicious use of typeclasses of which the param data is an instance, you don't need to keep on inventing different names for the same thing (eg to specify colour for the background of a label control in a GUI or colour for a font you could use the name "setColour" in both cases). (A possible disadvantage is the overhead of having to create a whole new modified version of the parameter record for each Customiser in the composition so if efficiency were an issue you'd have to see if the compiler could inline the composition of the customiser functions to make it as efficient as the built in multiple field record update using rec{a=a', c=c'} syntax)
To make things concrete, the example I'm really thinking of is a "send an email" function, which would take a subject, a body, a list of recipients, optional lists of cc and bcc recipients, an optional mailserver (default localhost), an optional port (default 25), and possibly optional authentication details. I found a couple of Haskell modules implementing a SMTP client, but they both just used a list of positional parameters, which I'm not really happy with. At the very least, I'd like to wrap them in a nicer interface for my code.
-- hidden from clients data EmailParams = EmailParams { subject :: String , body :: String , recipients :: [Recipient] , cc :: [Recipient] , bcc :: [Recipient] , mailserver :: Mailserver , port :: Port , authentication :: Authentication } -- record syntax elided to save space defaultEmailParams = EmailParams "" "" [] [] [] defaultMailserver defaultPort defaultAuthentication --a "Customiser" visible to clients setSubject :: String -> EmailParams -> EmailParams setSubject s ep = ep{subject = s} -- etc send :: (EmailParams -> EmailParams) -> IO () send f = sendInternal (f defaultEmailParams) where sendInternal :: EmailParams -> IO () sendInternal = ... -- In user code: main = send (setSubject "Test" . setBody "A test email") Brian. -- http://www.metamilk.com