"doctest" for haskell -- a good project?

Hello, I am an experienced programmer, currently learning Haskell. Currently I write many things in python. I use both the "doctest" and "unittest" modules extensively. As I write code, I simultaneously write doctest code in the doc strings to explain/set out the "typical narrative" of how the code is used. Then finishing off a module I write unittests for boundary conditions, more complex test cases, and generally code that would be annoying to write & read in doctests. I note that there is a unit testing framework for Haskell, but I don't see any doctest module. Might this be a good project? If so, suggestions as to resources would be greatly appreciated. I believe I can't just "introspect" Haskell modules to get at documentation/comments, like I can in python? (Why not? :)) I notice that there are a few "documentation generators". Should I try to write an extension of one of these? Haddock, for instance? Are there any Haddock developers hanging out on this list, to encourage or dissuade me? :) (And where is the Haddock doc for Haddock?) In any case, thanks in advance for any comments & advice. - Shaun Cutts

* Shaun Cutts
Hello,
I am an experienced programmer, currently learning Haskell. Currently I write many things in python. I use both the "doctest" and "unittest" modules extensively. As I write code, I simultaneously write doctest code in the doc strings to explain/set out the "typical narrative" of how the code is used. Then finishing off a module I write unittests for boundary conditions, more complex test cases, and generally code that would be annoying to write & read in doctests.
I note that there is a unit testing framework for Haskell, but I don't see any doctest module. Might this be a good project?
If so, suggestions as to resources would be greatly appreciated. I believe I can't just "introspect" Haskell modules to get at documentation/comments, like I can in python? (Why not? :)) I notice that there are a few "documentation generators". Should I try to write an extension of one of these? Haddock, for instance? Are there any Haddock developers hanging out on this list, to encourage or dissuade me? :) (And where is the Haddock doc for Haddock?)
In any case, thanks in advance for any comments & advice.
- Shaun Cutts
Did you try haddock? -- Roman I. Cheplyaka :: http://ro-che.info/ ...being in love is totally punk rock...

shaun:
Hello,
I am an experienced programmer, currently learning Haskell. Currently I write many things in python. I use both the "doctest" and "unittest" modules extensively. As I write code, I simultaneously write doctest code in the doc strings to explain/set out the "typical narrative" of how the code is used. Then finishing off a module I write unittests for boundary conditions, more complex test cases, and generally code that would be annoying to write & read in doctests.
I note that there is a unit testing framework for Haskell, but I don't see
There's a couple, QuickCheck is the best, it generalises unit testing to property testing with random, or not so random, data.
any doctest module. Might this be a good project? If so, suggestions as to resources would be greatly appreciated. I believe I can't just "introspect" Haskell modules to get at documentation/comments, like I can in python? (Why not? :)) I notice that there are a few "documentation generators". Should I try to write an extension of one of these? Haddock, for instance? Are there any Haddock developers hanging out on this list, to encourage or dissuade me? :) (And where is the Haddock doc for Haddock?)
In any case, thanks in advance for any comments & advice.
I'm not sure how doctest works, or how it would work in a Haskell setting, could you elaborate? One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function. > :doc map would result in: -- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...] marked up in ascii. I'm not sure if that's related to doctest, but it sure would be useful! -- Don

Hi
One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function.
> :doc map
would result in:
-- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...]
That will be in Hoogle 4, as it does already directly relate to what Hoogle has to do. As for doctest, i think that could be implemented as a --warn flag to Haddock, and might be a useful thing to have. For example "90% of the base library has documentation, please go add documentation to 'sinh'", as a warning message. Thanks Neil

On 22 mar 2008, at 13.17, Neil Mitchell wrote:
Hi
One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function.
:doc map
would result in:
-- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...]
That will be in Hoogle 4, as it does already directly relate to what Hoogle has to do.
As for doctest, i think that could be implemented as a --warn flag to Haddock, and might be a useful thing to have. For example "90% of the base library has documentation, please go add documentation to 'sinh'", as a warning message.
Though that would be a nice feature, too, I think you misunderstood what a doctest is. A doctest is a unit test in the documentation. This way it serves as a usage example as well as a formal specification of the semantics. I think we already have some way to run quickchecks inside documentation, but I can't remember what it was that could do that. / Thomas

Thanks for the quick responses. To elaborate on how doctests work in python: (Background: in python functions, classes & modules are all "objects" -- having attributes as well as (for functions) the ability to run them. They all have, in particular a documentation string associated with them. To get at these doctest module just loads the module, then examines its contents, looking for doc strings of contained functions & classes... so the language itself provides introspection capabilities: a strong point of python. Maybe Haskell should also have this :).) Doctests in python are bits of code in documentation strings specially annotated. In the docstring for a function "f", we might have (with apologies for python function call syntax :)):
f( foo ) bar
This means: run f( foo ) and check that (the string representation of ) the output is "bar". You can put any code in docstring. In effect, the doctest module turns what you have written into a little module, together with imports, etc. For continuation lines on blocks of code the convention is:
f( ... foo )
A blank line after a ">>>" line means that no output is expected (you defined a function, for example). If a blank line is part of the string representation of output, then it is represented, e.g.:
f( foo ) <BLANKLINE> bar
If result has lots of text, you can use
f( foo ) <ELLIPSIS> bar
This is helpful, e.g. if f is a helper function that catches an exception then prints it out; you don't want to check the resulting stack-trace -- just the part at the end about what exception was caught. There are lots of other switches & options, but no need to copy all of the details. (Anyway, by the time I get to worrying about them, I'll have my own opinions. :)) --------------------------------------- So Paul Johnson writes:
You need to support QuickCheck and Hunit
Haddock does not contain a full Haskell parser, which makes it difficult to extend in a principled way (or maybe I'm just not a good enough
In fact, at least in python, doctests are even more generic... They support "anything" -- though QuickCheck tests are also allowed. Does this approach seem ok/desirable here too? A bit worrisome info from Paul: programmer).
I also looked at using the Haskell parser in the libraries, but that doesn't capture comments. Associating a comment with a particular construct is not that simple either.
Hmm... this makes me think that the first thing would be for me to put in an option to capture comments in the parse tree in the parser. Then Paul and crew would hack the parser's use into Haddock. Then I'd extend Haddock to support doctests! How about that? Or actually I think its overkill. If I can hook into the processing of the comment (presumably only extended comments), I can piece together a fake "module". The "..." convention means I should know how to stitch together expressions without having to understand them. One problem is checking the "output" of an expression. If an expression has output, it would seem I should throw in a test (using QuickCheck?). Hmmm. - Shaun
-----Original Message----- From: Thomas Schilling [mailto:nominolo@googlemail.com] Sent: Saturday, March 22, 2008 9:54 AM To: Neil Mitchell Cc: Don Stewart; Shaun Cutts; haskell-cafe@haskell.org Subject: Re: [Haskell-cafe] "doctest" for haskell -- a good project?
On 22 mar 2008, at 13.17, Neil Mitchell wrote:
Hi
One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function.
:doc map
would result in:
-- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...]
That will be in Hoogle 4, as it does already directly relate to what Hoogle has to do.
As for doctest, i think that could be implemented as a --warn flag to Haddock, and might be a useful thing to have. For example "90% of the base library has documentation, please go add documentation to 'sinh'", as a warning message.
Though that would be a nice feature, too, I think you misunderstood what a doctest is.
A doctest is a unit test in the documentation. This way it serves as a usage example as well as a formal specification of the semantics. I think we already have some way to run quickchecks inside documentation, but I can't remember what it was that could do that.
/ Thomas

On Sat, Mar 22, 2008 at 8:39 AM, Don Stewart
Hello,
I am an experienced programmer, currently learning Haskell. Currently I write many things in python. I use both the "doctest" and "unittest" modules extensively. As I write code, I simultaneously write doctest code in the doc strings to explain/set out the "typical narrative" of how
shaun: the
code is used. Then finishing off a module I write unittests for boundary conditions, more complex test cases, and generally code that would be annoying to write & read in doctests.
I note that there is a unit testing framework for Haskell, but I don't see
There's a couple, QuickCheck is the best, it generalises unit testing to property testing with random, or not so random, data.
any doctest module. Might this be a good project? If so, suggestions as to resources would be greatly appreciated. I believe I can't just "introspect" Haskell modules to get at documentation/comments, like I can in python? (Why not? :)) I notice that there are a few "documentation generators". Should I try to write an extension of one of these? Haddock, for instance? Are there any Haddock developers hanging out on this list, to encourage or dissuade me? :) (And where is the Haddock doc for Haddock?)
In any case, thanks in advance for any comments & advice.
I'm not sure how doctest works, or how it would work in a Haskell setting, could you elaborate?
One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function.
> :doc map
would result in:
-- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...]
marked up in ascii.
I'm not sure if that's related to doctest, but it sure would be useful!
To be honest, I think this is what we should see when we type ":info map". Preferably we'd get a proper GUI version of GHCi which prints it out into a HTML box or something with proper hyperlinks and formatting. -- Sebastian Sylvan +44(0)7857-300802 UIN: 44640862

Hey Don,
On Sat, Mar 22, 2008 at 8:39 AM, Don Stewart
I'm not sure how doctest works, or how it would work in a Haskell setting, could you elaborate?
In a nutshell, Python doctest has the programmer put an example "interactive session" in a functions docstring. A doctest module then extracts those, tries running the function on the inputs and sees if it matches the output. Best shown by an example: def adder(a,b): """Returns the sum of the two arguments. >>> adder(10,10) 20 >>> adder("a","b") 'ab' >>> adder(10,"100") Traceback (most recent call last): File "test.py", line 6, in adder return a+b TypeError: unsupported operand type(s) for +: 'int' and 'str' """ return a+b if __name__ == "__main__": import doctest doctest.testmod() # Test this module Running this script with $ python test.py -v gives the following output Trying: adder(10,10) Expecting: 20 ok Trying: adder("a","b") Expecting: 'ab' ok Trying: adder(10,"100") Expecting: Traceback (most recent call last): File "test.py", line 6, in adder return a+b TypeError: unsupported operand type(s) for +: 'int' and 'str' ok 1 items had no tests: __main__ 1 items passed all tests: 3 tests in __main__.adder 3 tests in 2 items. 3 passed and 0 failed. Test passed. I.e it extracted the test cases from the sample interactive session and compared the real results.
One idea that does strike me is that it would be super useful to have the ability in ghci to extract the haddocks associated with a function.
> :doc map
would result in:
-- | 'map' @f xs@ is the list obtained by applying @f@ to each element -- of @xs@, i.e., -- -- > map f [x1, x2, ..., xn] == [f x1, f x2, .. -- > map f [x1, x2, ...] == [f x1, f x2, ...]
marked up in ascii.
I'm not sure if that's related to doctest, but it sure would be useful!
Python does exactly that and it is very very useful. I'd love to see that in GHCi. Arnar

arnarbi:
Hey Don,
On Sat, Mar 22, 2008 at 8:39 AM, Don Stewart
wrote: I'm not sure how doctest works, or how it would work in a Haskell setting, could you elaborate?
In a nutshell, Python doctest has the programmer put an example "interactive session" in a functions docstring. A doctest module then extracts those, tries running the function on the inputs and sees if it matches the output.
Ah yes. This is a great idea, and would be very useful. Haskell code is often documented with QuickCheck properties the code satisfies. Ensuring these are up to date is something that would be good to automate. It should be easier than in Python too, as fragments typically are pure, and don't require state to be initialised. -- Don

In a nutshell, Python doctest has the programmer put an example "interactive session" in a functions docstring. A doctest module then extracts those, tries running the function on the inputs and sees if it matches the output. Best shown by an example:
By the way, python does this by creating a temporary module from the docstring, then evaluating this. Is there a dynamic way in Haskell to evaulate a module? Or will I have to be content starting some sort of "subsession"... Which will make the doctest module less robust & portable. One idea would be just to produce a set of modules in the "core" doctest module. Then there would be a set of lightweight scripts for automating "produce & run" in various environments. But if there is a good "internal" way that I don't know about, I'm all ears. - Shaun

Shaun Cutts wrote:
I note that there is a unit testing framework for Haskell, but I don't see any doctest module. Might this be a good project? I once looked at doing this, but I didn't get very far.
Haddock is important here because you want to include the tests as part of the documentation. QuickCheck properties in particular closely resemble a formal specification. For a couple of examples, see my RangedSet package and Neil Mitchel's FilePath package. I manually copied the RangedSet tests into the Haddock documentation, while Neil wrote a small Haskell script to extract his tests from his documentation automatically. Unfortunately his script is tied to specific aspects of his FilePath package. Haddock does not contain a full Haskell parser, which makes it difficult to extend in a principled way (or maybe I'm just not a good enough programmer). The problem is you need to have several different new kinds of mark-up, and its not always clear what to do. You need to support QuickCheck and HUnit. Also possibly SmallCheck and some other similar unit testing cum specification frameworks. For QuickCheck you need to have type information for the tests. For instance the classic specification / test of "reverse" is:
prop_reverse xs xy = (reverse xs ++ reverse ys) == reverse (ys ++ xs)
Somewhere you need to flag that "xs" and "ys" are lists of integers. In the case of my RangedSets I needed to run the tests with both Integers and Doubles. I also looked at using the Haskell parser in the libraries, but that doesn't capture comments. Associating a comment with a particular construct is not that simple either. If you can manage this then it would be great. Paul.

Hi
I once looked at doing this, but I didn't get very far.
Me too, and I managed to get some way:
resemble a formal specification. For a couple of examples, see my RangedSet package and Neil Mitchel's FilePath package. I manually copied the RangedSet tests into the Haddock documentation, while Neil wrote a small Haskell script to extract his tests from his documentation automatically. Unfortunately his script is tied to specific aspects of his FilePath package.
Yes, the problem was that FilePath wants to execute half the tests twice with different modules, and half with just one namespace. As far as tests go, this is a very wacky requirement. I wanted to generalise it into a tool, but couldn't find a sensible generalisation that still worked with filepath - and hence didn't bother. I think the solution is to not permit the quirkyness of filepath, and write a general solution for everything else. As someone who has frequently considered writing this, even going as far as brainstorming on a whiteboard, I would be an enthusiastic user of this. I think the lack of this tool in Haskell is a big hole which someone needs to fill. I particularly like the facility in FilePath: -- > x == reverse (reverse x) -- > reverse "neil" = "lien" i.e. I can write both quickcheck properties (quantified over all single letter variables), or I can write an instance (like doctest in Python) Thanks, and good luck! Neil

resemble a formal specification. For a couple of examples, see my RangedSet package and Neil Mitchel's FilePath package. I manually copied the RangedSet tests into the Haddock documentation, while Neil wrote a small Haskell script to extract his tests from his documentation automatically. Unfortunately his script is tied to specific aspects of his FilePath package.
Yes, the problem was that FilePath wants to execute half the tests twice with different modules, and half with just one namespace. As far as tests go, this is a very wacky requirement. I wanted to generalise it into a tool, but couldn't find a sensible generalisation that still worked with filepath - and hence didn't bother. I think the solution is to not permit the quirkyness of filepath, and write a general solution for everything else.
Niel -- I understand your script is part of FilePath... might it be a good starting point for abstraction? Can you point me to it?
As someone who has frequently considered writing this, even going as far as brainstorming on a whiteboard, I would be an enthusiastic user of this. I think the lack of this tool in Haskell is a big hole which someone needs to fill. I particularly like the facility in FilePath:
-- > x == reverse (reverse x) -- > reverse "neil" = "lien"
i.e. I can write both quickcheck properties (quantified over all single letter variables), or I can write an instance (like doctest in Python)
Thanks, and good luck!
Neil
Thank you for the support... This might take me a while, I must warn, as this is an "after work" project, and I am a consultant, so "after work" often doesn't come :). But its certainly motivating to work on something right away that could be useful (to myself as well). - Shaun

Hi
Niel -- I understand your script is part of FilePath... might it be a good starting point for abstraction? Can you point me to it?
You are certainly welcome to start from it, if it is of any use to you: darcs get http://darcs.haskell.org/packages/filepath/ Then look in the test directory.
Thank you for the support... This might take me a while, I must warn, as this is an "after work" project, and I am a consultant, so "after work" often doesn't come :). But its certainly motivating to work on something right away that could be useful (to myself as well).
I will be greatful for anything that comes out - and if you don't get anywhere with the code but do come up with a design, you can always write a blog post on it so if it ever gets continued people can start from where you left off. Thanks Neil

Hi Shaun, I've read the whole thread till now. If you only look at the testing side Cabal is a possible target to run your tests. (I think you've already met it?) Adding documentation ficilities to ghci is nice, however my experience is that documentation is not complete everywhere. That's why I'm looking at source code directly (thus having doc strings if given else I can have look at source directly). One quick way to find the source location is using hasktags (comes with ghc). It's still some work to create tags for each package (that's why I've automated it within the nix distribution system). If you're interested send me a mail or contact me at #haskell. Marc W.

On Sun, Mar 23, 2008 at 5:09 PM, Marc Weber
Adding documentation ficilities to ghci is nice, however my experience is that documentation is not complete everywhere. That's why I'm looking at source code directly (thus having doc strings if given else I can have look at source directly).
This reminds me of a very handy feature in ipython (a custom python repl) - you can type name? which prints the docstring associated with name (which can be a function, module, class etc.) - but if you type name?? you get the docstring _and_ the source code (colorized and all :). I use it a lot when I'm experimenting in a interactive session. I could see something similar for haskell being very useful, to me at least. Arnar
participants (9)
-
Arnar Birgisson
-
Don Stewart
-
Marc Weber
-
Neil Mitchell
-
Paul Johnson
-
Roman Cheplyaka
-
Sebastian Sylvan
-
Shaun Cutts
-
Thomas Schilling