Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create ReadTheDocs project for unordered-containers. #249

Open
wants to merge 10 commits into
base: master
Choose a base branch
from

Conversation

m-renaud
Copy link
Collaborator

These docs walk a newcomer through the unordered-containers package.

Content includes:

  • Overview of what's available in the package
  • Related packages
  • Links to more resources
  • How to import and add a dependency on the package
  • Walk through common functions from HashSet and HashMap

Rendered docs can be found at https://m-renaud-haskell-unordered-containers.readthedocs.io/en/m-renaud-docs-user-guide/

Once approved these will be published at https://haskell-unordered-containers.readthedocs.io.

This resolves #173.

/cc @treeowl for review

@m-renaud
Copy link
Collaborator Author

m-renaud commented Apr 9, 2020

Friendly ping @treeowl :)

Copy link
Member

@emilypi emilypi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this looks great! A few comments below.

docs/conf.py Outdated Show resolved Hide resolved
docs/hash-map.rst Outdated Show resolved Hide resolved
docs/hash-map.rst Outdated Show resolved Hide resolved
docs/hash-set.rst Outdated Show resolved Hide resolved
Copy link
Member

@emilypi emilypi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that @m-renaud! it looks good.

@m-renaud m-renaud requested a review from emilypi May 29, 2020 17:53
@emilypi
Copy link
Member

emilypi commented May 29, 2020

@m-renaud why the change? HashMap.!? doesn't seem to exist for HashMap, and would be a total function if it did, meaning the docs would be incorrect. HashMap.! is the partial function, which could throw. The fact that !? doesn't exist is actually surprising to me, and I hope there's a PR out for it.

@m-renaud
Copy link
Collaborator Author

Oops, that was actually a mistake, I was looking at the wrong section of the docs. Undone.

@m-renaud
Copy link
Collaborator Author

Oh wait, you're right, !? doesn't exist either :/

@m-renaud
Copy link
Collaborator Author

@emilypi see #175.

@emilypi
Copy link
Member

emilypi commented May 29, 2020

It'll exist soon! Great. We can add more docs when it's merged. As is, with your changes here, we should be good to merge this.

@emilypi
Copy link
Member

emilypi commented May 29, 2020

@sjakobi your eyes would be appreciated.

@sjakobi
Copy link
Member

sjakobi commented May 30, 2020

Thanks for doing this, @m-renaud! :) I think this kind of introductory documentation is very important.

Nevertheless, I wish that this was integrated in the haddocks:

  • Better discoverability
  • 2 markup languages (Markdown and Haddock) instead of 3 (RST)
  • Higher likelihood IMO to see further improvements by other contributors
  • Less indecision where future documentation improvements should be added
  • No need to apply documentation updates in two places

Would you consider converting this into one or several tutorial modules in the style of Generic.Random.Tutorial or Pipes.Tutorial? I think this could at least partially be automated with pandoc.

@m-renaud
Copy link
Collaborator Author

Thanks for the feedback @sjakobi! I briefly considered putting the tutorial in the haddocks but opted for RST + ReadTheDocs for a few reasons:

  • Formatting: (imho) significantly nicer formatting, navigation, and search within the documents than you get with haddock
    • Specifically, the custom formatting for TIP, IMPORTANT, WARNING, etc. makes a pretty big difference in readability.
    • You can kinda tell that Haddock was designed with API docs in mind, and less so free form content.
    • Also, different renderings based on if you're browsing on hackage or stackage (I think stackage's rendering is much better)
  • Independent doc releases
    • No need to release a new version of the package to fix/modify/improve docs
  • RST is (ihmo) an easier format to write/read than Haddocks
    • TBH I find the haddock markup a little obtuse for writing mostly free-form content, looking at Tutorial.hs from pipes there's a weird mix of {-, >, four space indents, >>>, @ for formatting code, and the majority of the content needs to be indented since its mostly text. RST is incredibly similar to markdown, but with a slightly different syntax for links and x-refs.
  • Doesn't introduce an extraneous "not actually part of the package module", just for the purpose of creating a page in the docs.
    • This is another set of modules that needs to be parsed/built whenever you build the package. Obviously not a deal breaker, but at least for containers and unordered-containers which are a dependency of a large number of projects, introducing 4-8 additional modules to build (even if they're simple) every time their used seems non-ideal.
  • Branding: Haskell docs have a bad reputation of not being beginner friendly, and API docs are not always the ideal place to start if you're new (to Haskell, or a given package).
    • I was imagining an "intro docs" branding which is separate from API docs and has a consistent form and theme which brings some amount of familiarity, in this case the ReadTheDocs format and the green Haskell logo (green ~= fresh/new or something 🙃 )

To speak to the specific points you've raised:

Better discoverability

Having a sentence at/near the top of the entrypoint into the docs referring to the (external) docs doesn't seem much different than having a sentence which refers to the Tutorial module. (see the following in the containers package).

image

Also, SEO should take care of indexing it properly as well, for example here's a search for haskell containers tutorial:

image

2 markup languages (Markdown and Haddock) instead of 3 (RST)

That's a fair point, I don't think RST is that much different than Markdown, but its still slightly different.

Higher likelihood IMO to see further improvements by other contributors

I actually think its the opposite, with ReadTheDocs each page has a direct link to where you can edit the documentation right from the rendered docs, which I think actually lowers the barrier for making edits. Additionally, as I mentioned above, changing haddocks requires a new release of the package for the changes to take affect which may dissuade external contributors.

image

Less indecision where future documentation improvements should be added

Eh, I can buy that, but also a specific directory named docs/ in the top level of the package is pretty discoverable.

No need to apply documentation updates in two places

If you're adding/changing a function in a module, you still need to know to update the "intro docs", regardless of if that lives at Tutorial.HashMap or docs/hash-map.rst. /shrug

I'd love to hear your thoughts on my points above! In any case, if you feel strongly that this should live in a Tutorial module I can take a stab at doing so, but I think it will result in a sub-optimal final product as compared to ReadTheDocs.

@emilypi
Copy link
Member

emilypi commented May 30, 2020

Sorry for potentially expanding the scope of this already sizable PR, but @m-renaud, #175 is now in, if you want to add the docs for (!?) while we come to a decision.

@m-renaud
Copy link
Collaborator Author

No worries, it's always good to get more opinions :) And yeah I can do that, I'm presuming there a plan to cut a new release of the package soon so that the documented !? actually exists, yeah?

@emilypi
Copy link
Member

emilypi commented May 31, 2020

I should hope so 😄. I'm going to go through the merges since last release and put together a criteria for the next release. It should include the space leak fixes, the new combinators, and also most of these new doc changes.

@sjakobi
Copy link
Member

sjakobi commented May 31, 2020

You make good points @m-renaud! I'm very happy that you care and have thought about this stuff so deeply! 👍

I'll respond to some of your arguments:

  • Also, different renderings based on if you're browsing on hackage or stackage (I think stackage's rendering is much better)

Haddock's default style was changed with GHC-8.10 (or 8.8 already?), bringing it closer to Stackage's style.

Independent doc releases

  • No need to release a new version of the package to fix/modify/improve docs

I wouldn't mind making releases for documentation improvements, and will do so, if @treeowl gives me upload permissions.

Having a separate process for documentation changes, with separate permissions also makes things more complicated.

  • RST is (ihmo) an easier format to write/read than Haddocks

Haddock markup is admittedly tricky. At this point I'm sufficiently experienced with it that it's probably easier for me than writing RST though. In my experience RST has its pitfalls too though, and the subtle differences from Markdown don't exactly help.

Since this documentation is targeted at beginners, who are unlikely to know either Haddock or RST, I believe the ideal markup language to invite contributions would actually be Markdown!

Doesn't introduce an extraneous "not actually part of the package module", just for the purpose of creating a page in the docs.

  • This is another set of modules that needs to be parsed/built whenever you build the package. Obviously not a deal breaker, but at least for containers and unordered-containers which are a dependency of a large number of projects, introducing 4-8 additional modules to build (even if they're simple) every time their used seems non-ideal.

You're not wrong, but I also don't believe that the effect on compile times is large enough that this matters.

In fact, having these docs included in the haddocks, means that people can simply build them locally, e.g. with the classic stack haddock --open unordered-containers.

Branding: Haskell docs have a bad reputation of not being beginner friendly, and API docs are not always the ideal place to start if you're new (to Haskell, or a given package).

  • I was imagining an "intro docs" branding which is separate from API docs and has a consistent form and theme which brings some amount of familiarity, in this case the ReadTheDocs format and the green Haskell logo (green ~= fresh/new or something upside_down_face )

This is an intriguing idea! IMHO the better way to improve the reputation of Haskell library docs would be to improve the existing ones though. Even if you can pull off writing and publishing "intro docs" for a large swath of libraries, people will still encounter the (bad) old ones and might be put off.

Also, SEO should take care of indexing it properly as well, for example here's a search for haskell containers tutorial:

If we'd come to a point where searching for "unordered-containers docs" would give results for both the ReadTheDocs site and Hackage, I think that could cause confusion too…

Higher likelihood IMO to see further improvements by other contributors

I actually think its the opposite, with ReadTheDocs each page has a direct link to where you can edit the documentation right from the rendered docs, which I think actually lowers the barrier for making edits.

Not a bad point. I'm under the impression though that your intro docs for containers haven't received any contributions from other people, while the haddocks have received quite a few… Have other intro docs of yours seen more contributions?

Less indecision where future documentation improvements should be added

Eh, I can buy that, but also a specific directory named docs/ in the top level of the package is pretty discoverable.

I didn't mean an issue with discoverability here, but rather that when there are two separate "documentation trees", that might lead to some decision paralysis on where best to document certain things.

In any case, if you feel strongly that this should live in a Tutorial module I can take a stab at doing so, but I think it will result in a sub-optimal final product as compared to ReadTheDocs.

I feel bad to ask you for more after all the work you've already put into this! Nevertheless I think it's worth giving this a spin. Maybe just try it with a single page. If we still end up preferring the ReadTheDocs version, I'm definitely in favour of publishing it!

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

Haddock's default style was changed with GHC-8.10 (or 8.8 already?), bringing it closer to Stackage's style.

Oh great!

Having a separate process for documentation changes, with separate permissions also makes things more complicated.

The docs auto-rebuild whenever they're changed, so pretty minimal overhead (can also be done manually with a single button push).

If we'd come to a point where searching for "unordered-containers docs" would give results for both the ReadTheDocs site and Hackage, I think that could cause confusion too…

Eh, I don't think its uncommon for intro/beginner tutorials being hosted separately from API docs, as long as there's cross links I don't think it would be much of an issue.

I'm under the impression though that your intro docs for containers haven't received any contributions from other people

It actually has at least 5 unique contributions (haskell/containers@8b0b600, haskell/containers@58ca823, haskell/containers@0f61449, haskell/containers@3a343a3, haskell/containers@17494c9)!

The files were all moved into a different directory which apparently didn't maintain "history" but they still show up in "blame".

Nevertheless I think it's worth giving this a spin.

I migrated some of the content to a Tutorial and Tutorial.HashSet. Just my opinion, but the formatting and navigation are worse than on RTD. Specifically:

  • navigation to other parts of the tutorial require you to click on the "Contents" menu at the top then navigate to another module, you can't just quickly see and jump to other parts of the tutorial
  • Lack of IMPORTANT, TIP, WARNING formatting is unfortunate
  • Code formatting is not syntax highlighted
    • I misunderstood the markup, need to enclose symbols within @ blocks with quotes '
  • No search functionality
  • Only top-level headings, to have sub-heading you have to use bold/italic and manually attach hyperlinks, and I can't find a way to have them in the TOC
    • nvm, found subheadings in the Haddock docs

I'm also a bit of a Haddock noob, so its possible that there are ways to make the above better.

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

ReadTheDocs:

image

Haddocks

image

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

Open questions:

  • How can I get syntax highlighting in code blocks (@...@) that also maintains the HashSet. prefix? If I wrap a call to HashSet.foo with quotes it strips the HashSet. prefix in the rendered docs and just links to the function.

  • How can I get subheaders to show up in the TOC?

  • Any tips for improving navigation between tutorial modules?

@sjakobi
Copy link
Member

sjakobi commented Jun 1, 2020

Many thanks for giving this a spin, @m-renaud!

I'm having some trouble building your branch though:

$ cabal build unordered-containers
Resolving dependencies...
Build profile: -w ghc-8.10.1 -O1
In order, the following will be built (use -v for more details):
 - unordered-containers-0.2.10.0 (lib) (first run)
<snip>
Data/HashMap/Strict.hs:40:7-21: error:
    Not in scope: ‘findWithDefault’
   |
40 |     , findWithDefault
   |       ^^^^^^^^^^^^^^^

I'm quite mystified where that error comes from. CI is green too…

How did you build the haddocks locally?

@sjakobi
Copy link
Member

sjakobi commented Jun 1, 2020

I can reproduce the build failure in da5ac39, the parent commit of your branch. Rebasing on top of master fixes it.

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

Yup, looks like an old base CL, rebased and re-pushed commits.

Copy link
Member

@sjakobi sjakobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've taken a first look and added a few inline comments and suggestions that might help clear things up.

I see that you're struggling with the markup a bit. I'd suggest that you look at some existing tutorial modules like Pipes.Tutorial or Dhall.Tutorial for examples.

I think it's worth putting a bit more effort into this, so we get a somewhat fair comparison between Haddocks and ReadTheDocs.

If you have more questions, I'm happy to help!

Does that sound OK for you?


How can I get subheaders to show up in the TOC?

I think you have to use the top-level *, ** etc. headings for that.

Any tips for improving navigation between tutorial modules?

AFAIK there isn't much support for anything but module name references, e.g. "Tutorial.HashSet".

Support for anchors will improve with GHC 8.12 though: haskell/haddock#1179

Tutorial.hs Outdated Show resolved Hide resolved
order to keep the changes you need to assign it to a new variable. For example:

@
let s1 = 'HashSet.fromList' ["a", "b"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The easiest way to get qualified names is probably this:

Suggested change
let s1 = 'HashSet.fromList' ["a", "b"]
let s1 = "Data.HashSet".'HashSet.fromList' ["a", "b"]

I think this will only work with the full module name though.

There are also haddock options for this, but I'm not sure how reliable they are:

https://haskell-haddock.readthedocs.io/en/latest/invoking.html#cmdoption-qual

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, that's a little ugly, but it works.

@
import qualified 'Data.HashSet' as HashSet

let dataStructures = 'HashSet.fromList' ["HashSet", "HashMap", "Graph"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is in a @-block which supports (some?) Haddock markup, the strings are interpreted as module names! You can either

  • switch to > instead of @

  • use strings that aren't valid module names

  • escape the "s :

    Suggested change
    let dataStructures = 'HashSet.fromList' ["HashSet", "HashMap", "Graph"]
    let dataStructures = 'HashSet.fromList' [\"HashSet", \"HashMap", \"Graph"]

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

Thanks for the suggestions @sjakobi, I am indeed not experienced with Haddock markup 🙃. I think I'll have some time tonight to fix the markup in the existing docs and add a bit more content.

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 1, 2020

Support for anchors

Oh look! Haddock uses RST and ReadTheDocs for its user-facing docs 😜

@sjakobi
Copy link
Member

sjakobi commented Jun 2, 2020

BTW another IMHO pretty good-looking tutorial module is http://hackage.haskell.org/package/turtle-1.5.19/docs/Turtle-Tutorial.html. Of course, it's also by Gabriel Gonzalez!

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 2, 2020

Added some more content for comparison. Outstanding issues:

  • Multiple levels of sub-headings are pretty annoying since you need to split every piece into its own {- .. -} block, and the the title of the section is separate from the content
  • Lack of syntax highlighting in code blocks (strings, numbers, etc. aren't highlighted)
  • Special formatting for TIP, WARNING, etc. non existant
  • Navigation between different tutorial modules isn't possible in one click (need to know to click "Contents" and then find the "Tutorial" module in the list)

Do you think it would be helpful to solicit some more opinions?

@sjakobi
Copy link
Member

sjakobi commented Jun 2, 2020

I'm getting a build failure again:

$ cabal haddock --enable-documentation --haddock-for-hackage unordered-containers
Resolving dependencies...
Build profile: -w ghc-8.10.1 -O1
In order, the following will be built (use -v for more details):
 - unordered-containers-0.2.10.0 (lib) (first run)
Configuring library for unordered-containers-0.2.10.0..
Preprocessing library for unordered-containers-0.2.10.0..
cabal: can't find source for Tutorial/HashMap in .,
/home/simon/src/unordered-containers/dist-newstyle/build/x86_64-linux/ghc-8.10.1/unordered-containers-0.2.10.0/build/autogen,
/home/simon/src/unordered-containers/dist-newstyle/build/x86_64-linux/ghc-8.10.1/unordered-containers-0.2.10.0/build/global-autogen

cabal: Failed to build documentation for unordered-containers-0.2.10.0.

@sjakobi
Copy link
Member

sjakobi commented Jun 3, 2020

FWIW, I've started to make a list of tutorial modules here: https://github.com/sjakobi/awesome-haskell-tutorial-modules 😄

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 4, 2020

Oops, forgot to git add the new file.

@m-renaud
Copy link
Collaborator Author

m-renaud commented Jun 6, 2020

@sjakobi to throw another option out there:

Have the examples and walkthroughs in the module documentation as is done in Rust (see https://doc.rust-lang.org/1.5.0/std/collections/struct.HashMap.html for example), instead of a separate Tutorial module. Then we could improve the inline haddocks for the the individual functions to contain examples and better explanations, and then re-arrange the sections a bit. wdyt?

Aside: It may not be a great fit for this type of content, but the modules should probably have an example usage in them anyways.

Copy link
Member

@sjakobi sjakobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm submitting this review mostly to publish my two inline comments that I had written earlier.

I'm mostly in favour of the inline example version in #267 now.

My current impression of tutorial modules is that they are mostly used to introduce some kind of DSL. The u-c API is quite simple though – no real need for a full tutorial.

Comment on lines +19 to +28
@
import qualified Data.HashSet as HashSet

let s1 = HashSet.'HashSet.fromList' ["a", "b"]
let s2 = HashSet.'HashSet.delete' "a" s1
print s1
> HashSet.'HashSet.fromList' ["a","b"]
print s2
> HashSet.'HashSet.fromList' ["b"]
@
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This style seems rather unusual and also doesn't fit with the way most GHCi sessions look. The usual doctest-style seems clearer to me:

Suggested change
@
import qualified Data.HashSet as HashSet
let s1 = HashSet.'HashSet.fromList' ["a", "b"]
let s2 = HashSet.'HashSet.delete' "a" s1
print s1
> HashSet.'HashSet.fromList' ["a","b"]
print s2
> HashSet.'HashSet.fromList' ["b"]
@
@
>>> import qualified Data.HashSet as HashSet
>>> HashSet.'HashSet.fromList' ["a", "b"]
fromList ["a","b"]
>>> HashSet.'HashSet.delete' "a" s1
fromList ["b"]
@

Comment on lines +187 to +188
HashSet.fromList ["base", "containers", "QuickCheck"]
> fromList [,"containers","base","QuickCheck"]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
HashSet.fromList ["base", "containers", "QuickCheck"]
> fromList [,"containers","base","QuickCheck"]
HashSet.fromList ["base", "containers", \"QuickCheck"]
> fromList ["containers","base",\"QuickCheck"]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add external, beginner friendly docs
3 participants