Generic code... is it worth it?
chris hermansen
clhermansen@gmail.com
Mon Dec 1 00:28:34 GMT 2025
Jose and list,
On Sun, Nov 30, 2025 at 12:35 PM Jose E. Marchesi <jemarch@gnu.org> wrote:
>
> Hello people!
>
> So mnabipoor and I spent some time this weekend musing about generic
> code in general and Algol 68 in particular. We looked at Lindsey's
> sketch for modals published in the Algol Bulletin 37 and discussed its
> many problems and implementation difficulties. We also did a little
> survey of the techniques used by other programming languages to deal
> with generic code, monomorphization, and what not, and how well Algol 68
> suites itself to them (spoiler: not very well).
>
Where I'm coming from...
1. I am no kind of language designer / implementer; rather, I am a
language user, so my attitude to computer languages comes from using them
for day to day coding purposes:
1. At school in the 1970s I learned FORTRAN, IBM 360 assembly
language and PL/I reasonably well, PL/360, Algol W, Pascal and
Algol 68 to
the point where I could write reasonable code in them;
2. In my working life, I used FORTRAN with a macro processor for
quite a long time, then Pascal (after I ported the MTS Pascal compiler to
OS/VS1, continuing when I moved to our first Unix machine in 1984);
3. In the 1980s, I wrote a lexical scanner and recursive descent
parser for a kind of "parameter definition language" in Pascal;
4. In my first 20 years working on Unix, SunOS and Solaris, I learned
three fundamental lessons:
1. I was OK with C but I really hated its lack of guardrails;
2. Even though Pascal was quite restrictive (seriously - array
bounds as a part of the type definition?) over the course of time, its
strong typing, array bounds checking and other guardrails
caught a lot of
my programming errors before I ever ran the code;
3. I could do a surprising amount of work with very compact awk
programs, especially given its support for regular expressions and
associative arrays;
5. I started using Java in 1997 and have been a huge fan ever since,
and increasingly use Groovy especially for scripting;
6. In the early 2000s, I ported my (still useful) parameter
definition language scanner / parser to Java;
7. I have tried LISP, Go, Python, Julia, Scala and probably a few
others, all of which I try to avoid whenever possible;
8. I also try to avoid JavaScript as much as possible, though I will
certainly use it in preference to BASIC (that is to say, I would rather
code complicated stuff in Google Sheets using JavaScript than in Excel
using Visual Basic);
9. I have never bothered to learn any C++, Haskell, ocaml, Rust,
Perl, matlab, octave, Modula-2, Oberon, etc etc;
2. It's fair to say that in my 47 years of "life as a programmer", Java
(and Groovy) have completely erased any interest I have in Pascal or any of
its descendants, but I still have a very soft spot for Algol 68.
> To us, it is quite obvious that people is clearly willing to pay an
> _enormous_ toll for the generic programming capabilities in their
> programming languages of choice: incredibly complicated implementations,
> kilometric unreadable diagnostics that are basically useless,
> unavoidable and shameful gigantismus in the resulting programs, the
> consequential dependency on tools that really should not be necessary,
> like multi-threaded linkers... and many more unpleasantries.
>
I've never looked "under the covers" at the way Java handles generics, but
I can say I've used them a LOT and I really appreciate the strong
compile-time type checking they give me. In particular, I have:
1. written many 10s of data management applications using generics
together with hash maps, to map String keys to some kind of data structure;
2. occasionally run into a situation where I can use generics to give
myself generic classes that can be applied to several different data
structure configurations while ensuring maximal code reuse;
3. more recently but still fairly often, written applications that use
Streams to process structured data, requiring me to follow the use of
generics to code up my own BinaryOperator, Comparator and other bits of
machinery that enable the application of Stream classes to processing data
streams;
4. also more recently but still fairly often, using Function and
generics to code up lambdas.
I have not ever spent solid time studying the way Java implements streams
to the point where I can critique it for needless complexity or any other
overwrought unpleasantness, but I can say that:
1. doing real stuff with Stream in Java requires some learning, both of
the paradigm and the way it is implemented in (strongly-statically-typed)
Java;
2. once that threshold is passed, the resulting code doesn't feel
clumsy, fragile nor laden with technical debt / bad code smells.
So, while I bet your comment above, ie that there are some pretty unwieldy
generics implementations "out there", based on my significant personal
experience with generics in Java, it is possible to implement them in such
a way as to be really useful and that doesn't produce a lot of complexity.
Then we asked ourselves the (also obvious) question: once the blood
> (fat) price is paid, what do they use the expensive goodies for? In the
> vast majority of cases this is to implement stacks, lists, vectors, hash
> tables and other generic data structures, and also sorting routines, in
> comfort.
>
Don't forget user interfaces! And then please consider: if you eliminate
data structures, sorting, and user interfaces, you have eliminated a great
deal of what programmers actually do!
I think it's worthwhile considering what generics do, at a higher level:
they abstract away a lot of the "how" details, in the same way functional
programming proponents talk about the big advantage of functional
programming as putting the programmer in the position of always working on
"what" and not having to worry overmuch about the "how".
Going back to the simple example of hash tables - both Python and Groovy
provide a really compact way of initializing a hash map, adding elements,
looking things up... for instance in Groovy:
def myHashMap = ['am': 0, 'pm': 12]
Looking at that, I can assume that if I look up the value for key 'am' I
will get a time offset of 0 hours, whereas for the key 'pm', I will get a
time offset of 12 hours.
But Groovy won't complain, neither at compile time nor run time, if I try
to look up an integer key, whereas in Java
Map<String,Integer> myHashMap = Map.of("am",0,"pm",12);
if I subsequently code
var foo = myHashMap.get(42);
the compiler will reject it.
>
> This brought us to the next question: if your programming language of
> choice, whatever it is, provides good enough mechanisms to realize these
> concepts in a reasonable and convenient way, do you really need the
> generic programming features on top of them, considering how expensive
> they are? Is it worth it?
>
That depends on how much extra work (and potential bugginess) that is
introduced by not having them.
>
> Let's start with sorting. Sorting is a pretty generic operation,
> because the whole business only depends on an ordered set or domain and
> the ordering relationship between the elements in the set. It really
> doesn't matter what you are sorting, apart from given two elements of
> the set, being able to determine which one is bigger or lesser or equal.
>
> Suppose then that we write the following module:
>
> module QSort =
> def
> pub proc qsort = (int left, right,
> proc(int,int)int cmp, proc(int,int)void swap) void:
> begin
> proc partition = int:
> begin int pivot = right;
> int i := left - 1, j := left;
> while j <= right - 1
> do if cmp (j, pivot) <= 0
> then i +:= 1;
> swap (i, j)
> fi;
> j +:= 1
> od;
> swap (i + 1, right);
> i + 1
> end;
>
> if left < right
> then int pi = partition;
> qsort (left, pi - 1, cmp, swap);
> qsort (pi + 1, right, cmp, swap)
> fi
> end;
>
> skip
> fed
>
> The `qsort' routine takes an interval of indexes, that define the set of
> elements, a comparator routine that knows how to compare elements given
> their indexes, and a swapping routine that knows how to swap the
> elements given their indexes. This is how qsort can be used to sort a
> multiple of ints (similar examples coul be written to sort other kind of
> multiples, or elephans, or whatever):
>
> access QSort
> begin [5]int list := (5,1,100,-2,1);
>
> qsort (LWB list, UPB list,
> (int a, b) int: (list[a] > list[b] | 1 |: list[a] < list[b]
> | -1 | 0),
> (int a, b) void: (int t = list[a]; list[a] := list[b];
> list[b] := t));
>
> { Check the multiple values are indeed sorted. }
> for i from LWB list to UPB list - 1
> do assert (list[i] <= list[i+1]) od
> end;
>
> So given the perfectly generic definition of `qsort', it takes only
> three lines of perfectly clear and concise code in order to put it to
> good use. With no macros, with no templates. Of course, this is
> because of Algol 68's sheer expressiveness and nice abstractions. But
> it is Algol 68 we are talking about.
>
Ok so far, but if you had generics, you could use THE SAME qsort routine to
sort on string keys, date keys, etc. Yes, you can get some way toward this
thanks to Algol 68 providing you with operator overloading so that you
could define a COMPARE operator that works on int, another that works on
real, another on string, another on Date and so forth, which is probably a
good enough solution in this case, since indexes and keys tend to be
simple-ish types.
But if your COMPARE operator for date only works on year/month/day and
someone wants to include hours/minutes/seconds, they are going to have to
get at your code so they can add their own COMPARE operator for date-time,
or maybe date-time-with-timezone...
I think, anyway - maybe I'm missing something.
Then there are lists, vectors, hash tables, and the like "generic" data
> structures... the typical favorite applications of generic code
> features. Suppose we have a module that implement supercomplicated and
> genius-level hashing functions:
>
> module Hash =
> def
> pub int hash_table_size := 1008;
>
> pub proc hash_string = (string s) int:
> begin bits hash;
> for i to UPB s
> do hash := BIN (ABS hash * 613 + ABS s[i]) od;
> hash := hash AND BIN (ABS (BIN 1 SHL 30) - 1);
> ABS hash %* hash_table_size
> end;
>
> skip
> fed
>
> Then we could use them to build our own hash tables:
>
> access Hash
> begin mode Bucket = struct (string s, int i, ref Bucket n);
> ref Bucket no_bucket = nil;
> [hash_table_size]Bucket table;
>
> proc put = (ref[]Bucket t, string s, int v) void:
> (get (t, s) = -1 | int h = hash_string (s); t[h] := (s, v, n of
> t[h]));
>
> proc get = (ref[]Bucket t, string s) int:
> (ref Bucket p := t[hash_string (s)];
> while (p :/=: no_bucket) andth (s of p /= s) do p := n of p od;
> (p :/=: no_bucket | i of p | -1));
>
> put (table, "one", 1);
> put (table, "two", 2);
> assert (get (table, "one") = 1);
> assert (get (table, "two") = 2)
> end
>
> So we have implemented a perfectly functional hash table data structure
> in nine lines of code not counting empty lines (this table is on the
> stack, using the heap would require adding a couple of `heap' keywords
> but no additional lines). Again, it could be argued this is easy
> because of Algol 68's expressive power. But again, it is Algol 68 we
> are talking about.
>
Again, I agree BUT you're only allowing string keys above, and operator
overloading is only going to take you as far as however many key types you
can conceive of when you're implementing the hash tables in the first
place, not providing a way for me to easily add my own type of key.
As an aside, the problem is worse on the value side. I guess your hash
table of string -> integer could map to another table of integer ->
mystructure, with this second table living outside the hash table end of
things; but maintaining two data structures for the price of one is pretty
antithetical to "concentrating on the 'how' versus the 'what'".
>
> So yes, we could add modals to the language, implemented as templates or
> a more sophisticated and less gross solution like Swift does.
>
> But, is the price to pay for it _really_ worth it?
> Would really love to hear people's opinions about this..
>
> Salud!
>
Maybe the first question to ask is, "is there a really minimal modal kind
of thing that could be easily added to the language that deals with the
most obvious use cases for modals"?
I might have missed something in your arguments; if so I apologize in
advance!
--
Chris Hermansen · clhermansen "at" gmail "dot" com
C'est ma façon de parler.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/algol68/attachments/20251130/3ba7059c/attachment-0001.htm>
More information about the Algol68
mailing list