[darcs-devel] Darcs Argument Handling

Ben Franksen ben.franksen at online.de
Wed May 14 01:11:49 UTC 2014


Simon Michael wrote:
> On 5/7/14 3:46 PM, Ben Franksen wrote:
>>> One result of using
>>> an uber-record for all options is that every command now gets every
>>> option, even if they cannot use a particular option sensibly.
>>
>> Right. This is why I am not yet 100% sure it is the right solution. It
>> doesn't seem very elegant to pass every possible option to every command.
>> It also introduces lots of additional names (all the fields of the
>> record), though that can, of course, be controlled via import lists.
> 
> [...] I'm very interested to see what you come up
> with. And whether optparse-applicative is ready for prime time.

I have not pursued the idea of using optparse-applicative further.

Instead I have been working on new design for option specification that 
isn't too far away from how things are done now in darcs.

What bugs me most about the current state of affairs is this: On the one 
hand there is Darcs.UI.Arguments that defines the DarcsOption type that 
describes logically related flags and how they are parsed with GetOpt. Each 
command selects a list of these DarcsOptions to generate the help and for 
high-level information about groups of flags.

On the other hand, there is the code that actually parses the list of 
DarcsFlag to extract the higher level information out of it. This is 
currently strewn about the particular commands, Darcs.UI.Arguments, 
Darcs.UI.Flags, as well as Darcs.UI.Command and Darcs.UI.RunCommand (for the 
options common to all commands).

There is no link between these two pieces! Furthermore, Darcs.UI.Arguments 
defines a great number of names for values of type DarcsOption. And then, 
analogous (but slightly differing) names are defined elsewhere for the 
parsing functions.

For instance, in Darcs.UI.Arguments there is

lookforadds =
    DarcsMultipleChoiceOption
    [DarcsNoArgOption ['l'] ["look-for-adds"] LookForAdds
      "look for (non-boring) files that could be added",
     DarcsNoArgOption [] ["dont-look-for-adds","no-look-for-adds"]
      NoLookForAdds
      "don't look for any files that could be added [DEFAULT]"]

while in Darcs.UI.Flags there is

lookForAdds :: [DarcsFlag] -> RF.LookForAdds
lookForAdds fs | LookForAdds `elem` fs = RF.YesLookForAdds
lookForAdds _ = RF.NoLookForAdds

If one wants e.g. to change the default, one has to look up to differently 
named functions in two places, study the implementation, etc. Very difficult 
and error prone.

So I investigated how to change the DarcsOption type so that it somehow 
contains the parsing function, too. The problem with that is that the data 
returned from a parsing function has a different type for each option.

Remembering Oleg Kiselyov's work on final-tagless DSLs, I found this very 
inspiring page: http://okmij.org/ftp/typed-formatting/FPrintScan.html#DSL-FIn 

I adapted the solution to the problem at hand, simplifying where possible 
and adding appropriate examples, most taken 1:1 from the darcs code base. 
Also, after initial experimentation, I switched from the type class based 
representation to a record of functions. This has a number of advantages, 
one of them being that the ugly newtype wrapping and unwrapping can be 
avoided. This made the code much more transparent, so that I could recognize 
that the OptSpec type, together with an obvious unit and Oleg's combinator 
for chaining them, forms a category. Very nice.

Each option spec consists of a parser (flag list to result), an unparser 
(result to flag list), and a list of option descriptions to serve as input 
to GetOpt. The examples in the code should be instructive.

The prototype is attached. It is a self-contained module with a main 
function, its sole purpose is to demonstrate the principle. It also contains 
a proof for the associativity of the main option spec combinator.

I would love to get feedback on this!

Cheers
Ben

PS: One word to the extensions I used: NoMonomorphismRestriction, 
RecordWildCards, LambdaCase. Neither of them is essential in any way; the 
last two make the code that defines the concrete options a bit more concise 
and readable. The first is mostly for easier testing in ghci.

PPS: All type signatures in the code are inferred.
-- 
"Make it so they have to reboot after every typo." -- Scott Adams
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OptionsFT.hs
Type: text/x-haskell
Size: 7614 bytes
Desc: not available
URL: <http://lists.osuosl.org/pipermail/darcs-devel/attachments/20140514/be21c69c/attachment-0001.hs>


More information about the darcs-devel mailing list