Lisp HUG Maillist Archive

(Lisp-Hug) Re: how much clear code should be clear

Very subjective indeed...

One guy's "tight and concise" code is another guy's "incomprehensible 
nightmare"...  ...and vice-versa :)

Art, I actually prefer your first version - probably because it is 
considerably shorter. The knowledge that it also runs almost twice as 
fast as the second version is a big plus to me also, of course. But, 
I should, I suppose, ignore that here.

However, the fact that the second version has (Gasp!) a small comment 
section that explains what it does and, especially, a bit about how 
it does it makes it, by far, the most attractive to me if I had to 
read/maintain it a year from now after you (<hyperbole>) were run 
over by a truck careening down the streets of Moscow and were no 
longer available (</hyperbole>) -- [for those who don't know, Art 
lives in Moscow].

My view on this really complex question ("What is clean code?") is 
that the best code that I have seen over the years always has 
meaningful comments and, especially, "looks pretty".

Art, your code that I have seen *always* "looks pretty". Look at the 
first, shorter, version - it "looks pretty" and is a delight to read 
- the first is "prettier" then the second version because it is shorter.

If I can read source code without stumbling over weird or 
inconsistent indentions, meaningless variable names, or, especially 
in Lisp, strange macros that mangle, for no apparent reason other 
than "just for the hell of it", the natural flow and "feel" of Lisp, 
then I can understand just about anything.

To me, reading source code is mostly about recognizing patterns - 
after you have been in the game for awhile, certain patterns that you 
have seen thousands of times simply leap out at you (e.g., "(let 
(...) (cond ((...") - very similar to a good chess player that 
recognizes common (and not so common) patterns that the pieces make 
("Hmmm, look at that knight fork on the rook and bishop coming in two 
moves - such a pretty thing").

The converse question, "What is ugly code?" to me has the answer: 
Ugly code almost always has at least one of the following 
characteristics: weird and/or inconsistent indentions; meaningless 
variable names; {macro-induced} jarring syntax that destroys the 
usual Lisp patterns... ...and/or no comments...

Anything that lessens the pleasure of reading the patterns of a piece 
of code induces ugliness...  A necessary but slightly insufficient 
requirement for "clean code" is that it "looks pretty".

A subjective answer to a subjective question :)


Merry Christmas to everyone -

Regards from a Snowy Rocky Mountains...

Jack Harper
Evergreen, Colorado USA



At 12:50 PM 12/15/2011, Art Obrezan wrote:

>Hello -
>
>When I write code I try to write clear code. But the clearness of 
>code is a very subjective term. Sometimes in the pursuit of 
>clearness it feels like creating a clear mess at the end.
>
>For example, here is a code to convert an utf8 array of bytes into a 
>LW string:
>http://paste.lisp.org/display/126511
>
>It works for its intended use despite the fact that it has flaws; 
>also it is fast enough in comparison with native functions from the 
>external-format package. Being fast speed ups the code where it is used.
>
>Not so long ago I again needed the code to convert from utf8 bytes 
>to lisp strings with slight modifications in requirements. The code 
>above looked and felt like being not clear so I rewrote it. Now it 
>is like this:
>http://paste.lisp.org/display/126512
>
>It works. But there are much more code now. Also the new code is 
>about 1.8 times slower that the original code, and when I sprinkle 
>the original code with optimize directives the new code would be 
>2-2.5 times slower. Because I use both codes in a web application 
>server, being fast means some additional requests per second. But it 
>is still considerable faster on my data than functions from the 
>external-format package.
>
>
>Questions: 1) when you look at those pieces of code, what do you 
>perceive as a clean/bad code? 2) how do you keep your lisp code 
>clean and clear? 3) how do you know when to stop clearing code? or 
>not clear at all?
>
>Best,
>  Art


RE: (Lisp-Hug) Re: how much clear code should be clear

Hi all,

I'm still a relative CL novice, but this subject really resonates with me.

Most recently I've leaned toward the 'Lisp in small pieces' approach, as exhibited in the second example.  My general readings have suggested to me this is the 'Lisp way':  breaking a program up into many small functional pieces (of course, how many is too many?).  That whole 'functional composition' thing, where the pieces are recombined and possibly re-used elsewhere.

But evidently there is a performance hit with all the 'funcall overhead'?  So now I'm left wondering whether the second example is best for exploratory or pedagogic programming and the first for highly performant production code?  And indeed, perhaps there is no such thing as a 'Lisp way'.

Just ruminating.    :^)

Brian C.



RE: (Lisp-Hug) Re: how much clear code should be clear

> But evidently there is a performance hit with all the
> 'funcall overhead'?

To my knowledge for LW it's a very negligible performance hit. I draw this conclusion from disassembling with (disassemble) compiled functions and from knowing a basic stuff about the complex inner workings of the x86/x64 processors.

The performance hit for the my second example comes from the different algorithmic decisions. For example, in the function (position-in-range-p) I call the function (length) all the time which means for every byte fetch from the buffer. Just rewriting that function to have a buffer length as a constant value for a particular buffer could give a boost of about 20%. The second algorithmic decision that affected the speed is the double fetch of the first byte from each utf8 char sequence. And so on.

Also, if you are worried about the overhead of calling a lot of small function you can inline them. There is a "(declaim (inline .." construct in CL that tells the compiler to inline functions.

Also, I run tests based on Raymond Wiker's test from his email. To me it was like this:

My 1st version: 17s (or 14s when peppered with (declare))
My 2nd version: 33 s
Raymond's version: 21s
LW's version: 167s (using external-format:decode-external-string)

Mind, that's those tests are of a fictional problem. I mean that in my production code there is different context in which the function would be called and how many times and what importance it has in the project as a whole. Because of that I see no problem with losing some speed.

> So now I'm left wondering whether the second example is best for
> exploratory or pedagogic programming and the first for highly
> performant production code?

No. That example goes to my production code (the first one is already in a production code too). I did a rewrite from the 1st version because I needed some changes and the 1st version looked not "pretty" and I thought it was hard to add changes I needed.

My 1st and 2nd version do the same thing but with slight variation. The 2nd version can processes "broken" utf8 sequences. I needed that.

> And indeed, perhaps there is no such thing as a 'Lisp way'.

There is. :-)))

> Most recently I've leaned toward the 'Lisp in small pieces'
> approach, as exhibited in the second example.  My
> general readings have suggested to me this is the 'Lisp
> way':  breaking a program up into many small functional
> pieces (of course, how many is too many?).  That whole
> 'functional composition' thing, where the pieces are
> recombined and possibly re-used elsewhere.

I've learned "that way" from reading about the Forth programming language and it's philosophy. Chuck Moore has a point with Forth and its principles. And I think his idea of factoring  is more deep than the idea of refactoring.

> Just ruminating.    :^)

Me too :-)

Best,
 Art


Updated at: 2020-12-10 08:37 UTC