Index  Comments

As I've written three implementations of SHA1 in APL, Ada, and Common Lisp now, I've noticed judging each in terms of the other would be both educational and interesting and so am doing so; I recommend reading all three of the preceding articles beforehand, of course.

The APL operated in terms of bits, the Ada in words, and the Common Lisp by octets. This was chosen because of inconveniences APL posed and later affected the other two. Common Lisp's CLOS has issues with its VECTOR type, as the only required specialization for method dispatch is BIT-VECTOR, and the Ada using words for convenience, efficiency, simplicity, and other reasons made it reasonable for my Common Lisp to merely use octets, which is also most well-supported in that. These three units were chosen due to being most innate, common, and that which SHA1 is defined in terms of.

The APL is easily the shortest and most concise, followed by the Common Lisp, and leaving Ada as the largest, with the latter two also being closest in size. The core of this algorithm is that partial hashing and this was four dozen lines of Common Lisp, twenty-seven for Ada, and a mere seven of APL; the Common Lisp had FLET for those K and F functions, however, whereas these were nine and eight for Ada and an additional three and four of APL; notable for the APL is six of these lines are header or footer and which must be on their own lines, meaning the APL is six shorter, when those are omitted. The padding functionality is thirty-nine of Common Lisp, twenty-nine for Ada, and another three APL, where two are header and footer. The APL was expected to be most concise and Ada most verbose.

According to purpose, the APL was merely meant to exist and concisely. Both the Ada and Common Lisp were intended to serve as general libraries exposing their functionality and to exemplify respective benefits and good design according to each language.

In design, the Ada received by far the most thinking, as I agonized over every detail of the package specification, with the intention to never need to change it; I'm particularly proud of how my names were sensible and consistent, with the exception of the SHA1.Pad parameter names for clarity. There was in comparison no design to the APL beyond separating and naming functions and none of even these were chosen to never need modification on a whim. The design of the Common Lisp merely mimicked the Ada in every feasible respect but needed no special naming for parameters nor types, as such details aren't accessible in the same ways or weren't needed in any way. The design of the Ada SHA1.Pad was chosen by being recognized as that least error-prone method, despite not being most space-efficient, and this basic decision influenced the other two, which return two blocks as necessary, although the Common Lisp is superior in a way, by permitting the extra block to be passed or allocated as needed, and by using the multiple-values system unlike the Ada out parameter; the APL result is shaped based on which case has occured and can be handled rather transparently.

In terms of comprehensiveness, the APL is also least. The base design of all three must provide for hashing without all data available and in cases where it's; the former is all the APL provides. The Ada provides all for convenience and, as the result types are private, several conversion functions; further, the type for partial and finished results are different, to help prevent mistakes. Lastly, the Common Lisp provides the same as the Ada, across more types, but uses no custom or private types and has fewer conversion functions due to this.

Now in considering ease of use, the Common Lisp is likely superior, by virtue of having both an REPL and methods for hashing streams and those of pathnames, along with using the common octet unit. The APL only provides the basest functionality and, despite also having an REPL, is noticeably lesser to the Common Lisp. The Ada, despite using the word unit and lacking an REPL, is in many respects less arduous than the APL and provides more comprehensive functionality; all that's needed to add ease is functionality to intake different units into a buffer and partially hash them, which is simple. All of the implementations ignore message sizes greater than the limit.

The major differences not yet listed include: the Ada performs no dynamic allocation nor exposes any failure cases; the APL was in many respects the easiest to write, it's so small I felt no compulsion to design it ahead of time, as it could very easily be changed; the Ada is my most heavily commented and the hardest to misuse; the Common Lisp is the easiest to add to; and the Ada was by far the most difficult to write, with its making so many aspects of the design I'm able to ignore in those others part of the interface, such as parameter names, along with needing to define my own types and decide which should be private or not, yet this also led to the pleasant Status and Digest type difference.

The APL pad is delightfully different from the other two, as its conditional behaviour involves zero and final shaping, whereas the other two use a pattern-based approach on the bit-length and are both rather the same, with minor differences mainly due to the language differences.

The APL and Common Lisp both have manually-inlined rotation in the partial hashing; the Ada doesn't, due to using a package privately for efficiency and its leagues less leisurely bit-manipulation; the APL trivially does this with its take and drop and the Common Lisp uses the DPB, LDB, and MASK-FIELD functions for its main bit manipulations, with the last noticeably lacking in Ada. How amusing it's easier to manipulate bits in Common Lisp than Ada. The body of the Ada was nicely aided by defining a private type, Cycle, which took the precise range demanded by the partial hashing, permitting this to be expressed succinctly and with more guarantees of correctness.

In sum, I feel I've written an idealized Ada implementation of SHA1 and my only expected changes are perhaps collecting it into a comprehensive SHA library, leaving this as a choice; I feel improved as an Ada programmer after this. I'm proud of the APL for its conciseness and the fun with writing it, although I'm not certain if I'd purport to be a better APL programmer. The Common Lisp lies between the others, as I'm pleased with the result and certainly intend to use it, along with having in mind plenty of neat tricks I can use it with, but I don't feel the overwhelming sense of satisfaction for its design nor do I feel I've become a better Common Lisp programmer, as I'm already experienced far past this level. In any case, this was fun and I'd like to do this with other programs.