"No, it's purely functional"
or, I will have none of your State monad, being functional anti-pattern the sixth.
Shortly after arriving at my previous employer, I was talking with oen of the senior developers, and some piece of state passing code came up. Having recently discovered the exciting world of the Monad Transformer Library, I said, eagerly, "Oh, are you using the State monad for that?" To which he replied, "No, the code is all purely functional," and seemed quite proud of himself. At the time, I was quite confused. Later, I became more confused, when it became apparent that every function in his code started:
function :: AppEngine -> ... -> STM stuff
function engine ... =
Of course, he rarely actually wanted the AppEngine. Frequently he actually wanted the DataStore, which lived inside a GenericEngine, inside the AppEngine. So, each function contained the line
where dataStore = store (genericEngine engine)
By this time, readers will presumably have wondered what is done with an AppEngine or its contents, since they seem to go in but not come out. However, as luck would have it, the contents of AppEngines et. al. were all TVars, so they could be updated using writeTVar, without having to worry about polluting your code with icky, icky monads.
I was more perplexed. However, as annoying as passing the engine as the first parameter to everything was, it was at least fairly obvious, even without being too familiar with the code in question.
On the other hand, a later example of this behavior I ran across was somewhat harder to decode. In this case, I was translating a set of generic action descriptions into situation specific information. The meat of the code had been written elsewhere, and I was given some fairly simple tools: an ActionSegment data structure, and a function:
makeSpecific :: Point -> ActionSegment
-> ActionSegment
makeSpecific startingPt segment = ...
Lest this seem somewhat confusing, both generic and specific actions were represented as lists of action segments. The difference was that the generic ones had fields with relative values, and the makeSpecific function ferretted out those values and replaced them with non-relative variations. The starting point argument also confused me a bit, since each ActionSegment included a starting point already, in non-relative form. However, after scouting around a bit for a use of startingPt, I noticed that the Point type implemented the (or not) pattern, so I happily passed NoPoint and ran it to find out what happened. And, as far as I could tell, it worked wonderfully. Generic fields became specific, I could generate the information I needed from there, and so on.
As it happened, though, subtle bugs started appearing. I eventually traced them back to the startingPt field. It turned out that when I got a list of ActionSegments, only the first had a valid starting point field. The starting points of the remaining ActionSegments had to be manually initialized to the computing finishing points of the previous segments, using the argument to makeSpecific. (As it happened, it was also advisable to pull the valid starting point out of the first segment and pass it to makeSpecific, since said function would happily overwrite the valid value in the first segment.) To finish complicating things, ActionSegments could contain sub-segments, so to speak, which had similarly fascinating starting and ending point relationships with their parents.
Some time later, I was talking to the original developer, and mentioned casually having discovered that feature late one night. Oh, yeah, he explained, well, the finishing point was in the result already so he didn't want to waste memory by returning it again.

0 diversions:
Post a Comment