Hal Daume III writes:
Hi all,
I'm looking for some advice on what's the cleanest way to implement something. The basic idea is that I have a task to solve, T. There are many steps to solving this task, but they can be broken down into a small list of elementary steps:
- prepareData - initialize - doThingOne - doThingTwo - getResults
In similar situations, especially if there is more than one useful way to use the various parts of an algorithm, I used often prefer existentials: data Model = forall markup table alignments. Model { prepareData :: Data () -> Data markup, initialize :: Data markup -> ST s table, doThingOne :: Data markup -> table -> ST s alignments, doThingTwo :: Data markup -> alignments -> ST s table, getResults :: Data markup -> table -> alignments -> String } So each model-module only needs to export one model record and the standard algorithm can be implemented easily: (simplified, not checked for stupid errors) execModel model data = let mark = prepareData model $ data in runST $ do tab <- (initialize model) mark align <- (doThingOne model) mark tab res <- (doThingTwo model) mark align return $ (getResults model) mark res align Of course, by using existentials, no model internal type can escape.
Now, I want in my executable my user to be able to say "-model=0" and so on in the command line and for it to use the appropriate model. Each of these models will go in a separate module.
This way one can even program a top-level like doForAll data = map (\m -> execModel m data) [model0, model1, model2, ...] which is rather useful. Regards, Harald