<div dir="ltr"><div>Hello chums,</div><div><br></div><div>I&#39;ve been playing around with an idea, something that has obvious pros</div><div>and cons, but I&#39;ll sell it to you because there might be some positive</div>

<div>ideas out of it. Consider the following operator:</div><div><br></div><div>    {-# LANGUAGE TypeOperators, DataKinds, KindSignatures #-}</div><div><br></div><div>    module Docs where</div><div><br></div><div>    import GHC.TypeLits</div>

<div><br></div><div>    type a ? (sym :: Symbol) = a</div><div><br></div><div>First I&#39;ll describe how I&#39;d want to use this and then what I think</div><div>are the advantages and disadvantages.</div><div><br></div>

<div>I call this (?) operator “the documentation operator”, to be used for:</div><div><br></div><div>* Things that either don&#39;t belong or can&#39;t be encoded in the type</div><div>  system, or for things need to be in English.</div>

<div>* Things that cannot be encoded in Haddock.</div><div><br></div><div>The simple case of ye olde days:</div><div><br></div><div>    -- | Lorem ipsum dolor sit amet. Suspendisse lacinia nibh et</div><div>    --   leo. Aenean auctor aliquam dapibus.</div>

<div>    loremIpsum :: Int -&gt; Int -&gt; String</div><div><br></div><div>Which has since been somewhat evolved into:</div><div><br></div><div>    loremIpsum :: Int    -- ^ Lorem ipsum dolor sit amet.</div><div>               -&gt; Int    -- ^ Suspendisse lacinia nibh et leo.</div>

<div>               -&gt; String -- ^ Aenean auctor aliquam dapibus.</div><div><br></div><div>But could now be written:</div><div><br></div><div>    loremIpsum :: Int    ? &quot;Lorem ipsum dolor sit amet.&quot;</div><div>

               -&gt; Int    ? &quot;Suspendisse lacinia nibh et leo.&quot;</div><div>               -&gt; String ? &quot;Aenean auctor aliquam dapibus.&quot;</div><div><br></div><div>Here is a contrived case I&#39;ll use later on:</div>

<div><br></div><div>    data Person = Person</div><div><br></div><div>    describeAge :: Int ? &quot;an age&quot; -&gt; String ? &quot;description of their elderliness&quot;</div><div>    describeAge n = undefined</div><div>

<br></div><div>    personAge :: Person ? &quot;a person&quot; -&gt; Int ? &quot;their age&quot;</div><div>    personAge = undefined</div><div><br></div><div>One could also encode previously informal specifications more formally,</div>

<div>so that</div><div><br></div><div>    -- | The action &#39;hFlush&#39; @hdl@ causes any items buffered for output</div><div>    -- in handle @hdl@ to be sent immediately to the operating system.</div><div>    --</div>

<div>    -- This operation may fail with:</div><div>    --</div><div>    --  * &#39;isFullError&#39; if the device is full;</div><div>    --</div><div>    --  * &#39;isPermissionError&#39; if a system resource limit would be exceeded.</div>

<div>    --    It is unspecified whether the characters in the buffer are discarded</div><div>    --    or retained under these circumstances.</div><div>    hFlush :: Handle -&gt; IO ()</div><div>    hFlush handle = wantWritableHandle &quot;hFlush&quot; handle flushWriteBuffer</div>

<div><br></div><div>with</div><div><br></div><div>type Throws ex (docs :: Symbol) = docs</div><div><br></div><div>could now be written</div><div><br></div><div>    hFlush :: Handle ? &quot;flush buffered items for output on this handle&quot; -&gt; IO ()</div>

<div>      ? Throws IsFullError &quot;if the device is full&quot;</div><div>      ? Throws IsPermissionError</div><div>               &quot;if a system resource limit would be exceeded. It is \</div><div>               \unspecified whether the characters in the  buffer are \</div>

<div>               \discarded or retained under these circumstances.&quot;</div><div>    hFlush handle = wantWritableHandle &quot;hFlush&quot; handle flushWriteBuffer</div><div><br></div><div>With this in place, in GHCi you get documentation &quot;lookup&quot; for free:</div>

<div><br></div><div>    &gt; :t hFlush</div><div>    hFlush</div><div>      :: (Handle ? &quot;flush buffered items for output on this handle&quot;)</div><div>         -&gt; (IO () ? Throws IsFullError &quot;if the device is full&quot;)</div>

<div>            ? Throws</div><div>                IsPermissionError</div><div>                &quot;if a system resource limit would be exceeded. It is unspecified</div><div>                 whether the characters in the  buffer are discarded or retained</div>

<div>                 under these circumstances.&quot;</div><div><br></div><div>And you get function composition, or “documentation composition” for free:</div><div><br></div><div>    &gt; :t describeAge . personAge</div>

<div>    describeAge . personAge</div><div>      :: (Person ? &quot;a person&quot;)</div><div>         -&gt; String ? &quot;description of their elderliness&quot;</div><div><br></div><div>We could have a :td command to print it with docs, and otherwise docs</div>

<div>could be stripped out trivially by removing the ? annotations:</div><div><br></div><div>    &gt; :t describeAge . personAge</div><div>    describeAge . personAge</div><div>      :: Person -&gt; String</div><div>    &gt; :td describeAge . personAge</div>

<div>    describeAge . personAge</div><div>      :: (Person ? &quot;a person&quot;)</div><div>         -&gt; String ? &quot;description of their elderliness&quot;</div><div><br></div><div>You could even add clever printing of such “documentation types”:</div>

<div><br></div><div>    &gt; :t hFlush</div><div>    hFlush</div><div>      :: Handle — flush buffered items for output on this handle</div><div>      -&gt; IO ()</div><div>    Throws IsFullError if the device is full&quot;</div>

<div>    Throws IsPermissionError if a system resource limit would be</div><div>      exceeded. It is unspecified whether the characters in the buffer</div><div>      are discarded or retained under these circumstances.&quot;</div>

<div><br></div><div>Unfortunately it doesn&#39;t work with monadic composition, of course.</div><div><br></div><div>So here are the advantages:</div><div><br></div><div>* You get parsing for free (and anyone using haskell-src-exts).</div>

<div>* You get checking for free (i.e. GHC can check that IsFullError exists</div><div>  for you).</div><div>* You get a continuity of documentation through your operations</div><div>  including composition.</div><div>* You can extend the &quot;documentation language&quot; easily by just defining</div>

<div>  some types (like the Throws I used above). SeeMore, Author,</div><div>  Deprecated, etc. Whatever.</div><div>* You can print out some helpful looking documentation in GHCi based on</div><div>  these simple types.</div>

<div>* There&#39;s no longer this informal &quot;it might throw this exception&quot; kind</div><div>  of pros we&#39;re forced to write.</div><div>* It could also be used for annotations other than pure documentation,</div>

<div>  including testing. E.g. add a Testable &quot;property&quot; and then your test</div><div>  framework can search for functions with this Testable annotation.</div><div>* Free of Haddock&#39;s syntax.</div><div><br>
</div>
<div>Here are the disadvantages:</div><div><br></div><div>* It doesn&#39;t work for types.</div><div>* Writing big pros inside a string can be boring without a decent</div><div>  editor.</div><div>* The neat composition trick only goes so far.</div>

<div>* There might be a compilation overhead.</div><div>* It would require an updated GHCi to strip them out when not wanted.</div><div>* Requires GHC 7.6.1+.</div><div><br></div><div>Conclusions:</div><div><br></div><div>

What we have now for documentation is pretty good, especially generated</div><div>documentation. Compared to other languages Haskell is quite well</div><div>documented, I feel. But we can do more with it. In some languages,</div>

<div>documentation is built into the language. You can ask for documentation</div><div>inside the REPL, it belongs to that piece of code. It shouldn&#39;t, I don&#39;t</div><div>think, be left as a code comment which is essentially whitespace as far</div>

<div>as the compiler is concerned.</div><div><br></div><div>Two sweet ideas that I like from the above are:</div><div><br></div><div>* The checking by GHC.</div><div>* The extension of the &quot;documentation language&quot;, with the ability to</div>

<div>  formalize things like what exceptions are thrown.</div><div>* Composing functions generates &quot;new&quot; documentation that still makes</div><div>  sense.</div><div><br></div><div>Thoughts?</div><div><br></div>
<div>
Ciao!</div></div>