[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: Documentation, Literate Programming and the Real World
- From: Sean Conner <sean@...>
- Date: Thu, 6 Oct 2016 06:13:12 -0400
It was thus said that the Great sur-behoffski once stated:
>
> On the code comment front, I rejected Web/FunnelWeb as too alien for most
> people, but tried to come up with a soft literate-inline version. Also, I
> decided to used Camel Case names, mainly as I believed that they are
> generally easier to read than all-uppercase or all-lowercase names.
I never cared for Literate Programming (and I even own _Literate
Programming_ by Knuth!) because it never seemed appropriate for the code I
write. I can see it being good for teaching purposes (maybe) or for code
that isn't expected to change (like TeX or METAFont).
But in my world, code changes. My own blogging engine [1], which I've
used since 1999, has not one original line of code from 1999 running. Every
line has changed over the past 17 years [2], with features coming (pinging a
web blog service around 2003; posting to Facebook around 2010) and going
(pinging a web blog service, about 2008 I think, because it wasn't worth it
anymore; posting to Facebook around 2015 because of changes Facebook made
that I couldn't handle). Any essay formed documentation about the code
would probably be laughably out of date.
> For each paragraph, I write a comment as if I'm explaining the code to an
> abstracted group of people as I'm going, using "we" quite often.
I'm not a real stickler for comments. I tend to comment why I do
something, rarely how I do something. For example (comments pulled from my
blogging engine):
bool tumbler_new(tumbler__s *const tum,const char *text)
{
/*----------------------------------------------------------------------
; all variables are defined here, even though u2, u3, u4, segments and
; part aren't used until tumbler_new_range. It appears that defining them
; there causes a compiler error. It's either a bug in the version of GCC
; I'm using, or you can't declare variables after a goto target label.
;
; I'm not sure what the case is here, so I'm moving them up here.
;----------------------------------------------------------------------*/
struct value u1;
struct value u2;
struct value u3;
struct value u4;
bool part = false;
assert(tum != NULL);
assert(text != NULL);
/* rest of function deleted */
(As an aside: I'm also not afraid of long functions, as long as it's doing
a single job. The above tumbler_new() function is 435 lines long, but it's
doing a particularly knarly parsing job [3]. I've marked sub-sections in
the function. I didn't make separate functions as because too much state
would need to be passed around and it would, I feel, complicate some already
complicated code).
or, to bring this into Lua, from my LPeg-based email parser [4]
local R =
{
-- -----------------------------------------------------------------------
-- These are being defined here instead of in the re portion because of a
-- rule limitiation in LPeg 0.12 (default of 200, requires a recompile to
-- extend). Moving the header names out to here will let this work under
-- LPeg 0.12, while still working back to LPeg 0.10.2.
-- -----------------------------------------------------------------------
RETURN_PATH = H "Return-Path",
RECEIVED = H "Received",
As you can see, I try to give the "why" I do something, not the "how".
> For example, some stripped-down regular expression lexing, in C):
>
>
> ------------------------ (cut here) --------------------------
>
> bool
> ProcessREPattern(char *pPattern, int PatternLen)
> {
> /* [...] Some initialisation code elided for brevity [...]. */
>
> /* Loop through the pattern bytes, converting to tokens, including
> handling special characters as appropriate. */
> for (;;) {
To me, this comment seems unnecessary. *Of course* we're looping through
a pattern. The function itself is called *ProcessREPattern*, is it not?
> /* Have we reached the end of the pattern? */
> if (pPat == pPatEnd) {
Again, this comment is just mimicking the code (you picked good names, so
points for that). Also, why not?
while (pPat == pPatEnd)
{
instead of the infinite "for() if" combo?
> /* Yes, break out of this loop so we can do some
> finalisation and sanity checking. */
> break;
> }
>
> /* Get the next character of the pattern. */
> Ch = *pPat++;
More pointless comments (is this code for a class or something?)
> /* Is this the start of a bracket expression? */
> /* FIXME: Escape may toggle character interpretation here.
> */
The first line is bad. The second line is good.
> if (Ch == '[') {
> /* Yes, this is a big job: Use BracketExp(). We
> also
> need to hand in current position, and to get back
> how many chars the bracket expression consumed.
> The
> function outputs tokens directly, as required.
> */
This comment ... borderline for me. It seems to partially repeat the code
in English, but also explains stuff that might not be apparent.
-spc
[1] https://github.com/spc476/mod_blog
http://boston.conman.org/about/
[2] But not the storage format. Sure, it has been extended, but my code
from 1999 would still work.
[3] https://github.com/spc476/mod_blog/blob/0b9522e3032e11059458dff954dc429a99d77eb5/src/wbtum.c#L152
for all the gory details.
[4] https://github.com/spc476/LPeg-Parsers/blob/ee63df57826fd5ef62eac5669b444e5d7c9da6ff/email.lua#L527