
On Mar 12, 2008, at 6:34 AM, Brandon S. Allbery KF8NH wrote:
On Mar 11, 2008, at 14:27 , Donn Cave wrote:
readLDAPMessage s = let [(_, msgID), (tag, body)] = berList s in LDAPMessage (berInt msgID) (readResponse tag body)
I go on to account for all the LDAP stuff I need in about 60 lines of that kind of thing, 1/3 of it devoted to declarations of the data types, and it isn't dense code, it's ... essentially declarative, in a simple, straightforward way, almost as if I copied it directly from the RFC.
Is it `total'? No way! To get there, it seems to me I'd have to double the code, and significantly distract from its real sense.
You might want to think about the monadic use of Maybe/Either (or more generally MonadError), which abstracts away the checking and tracking into (>>=). The error handler is then at the point where values are injected into / retrieved from the monadic exception, similar to catch (...).
Sure. It isn't a lot of code, so I subjected it to Either-ization as an experiment, and I did indeed take the monad procedural route. The example function above became readLDAPMessage s = do [(_, msgID), (tag, body)] <- berList s >>= exactLen 2 i <- berInt msgID r <- readResponse tag body return (LDAPMessage i r) ... and the end result, applying this style across a number of related functions, was no more than half again as much code, and I guess not severely unreadable. Maybe it depends on whether a procedural style suits you. There may be clever ways to torture this logic into an even smaller format, but since the original is the clearest expression of the protocol and its caller is almost guaranteed to have an exception handler anyway -- in my opinion, it was a silly exercise, I'll throw the code away. The Either version forces strict evaluation, true? Let's say for some reason the caller actually uses only the first part, the LDAP message ID, then we don't really need to validate and decode the whole message, but if I were to Either-ize it, then it has to go the whole distance before we know it's Right and not Left? And likewise for every value of every attribute in the message. What I naively picture as the next step, where pure code can handle exceptions, is an _implicit_ evaluation monad like Either. Pattern matches then throw exceptions in the sense described in Control.Monad.Error, which may be caught in pure code by optionally making the monadic type explicit, or otherwise are converted to exceptions in the sense of Control.Exception and caught in the IO monad. I suppose this would not force evaluation, because it's fine grained - I don't fold (Right i) and (Right r) into (Right (LDAPMessage i r)), etc., but rather you may encounter a (Left _) anywhere in there. At least this would let us rationalize the use of 'exception' with two radically different meanings between Control.Monad.Error and Control.Exception. Donn Cave, donn@avvanta.com