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