Generics, modals, "self", introspection, mode Anything
Jose E. Marchesi
jemarch@gnu.org
Sun May 10 11:25:28 GMT 2026
Hello Chris.
> Good evening everyone,
>
> A few topics that are picked up and put down every so often, some mentioned
> in the meet-up, that I would like to see discussed more fully...
>
> I haven't worked with generics nor modals in languages other than Java, so
> my perspective is not particularly rich. Nevertheless...
>
> Java makes extensive use of generics to facilitate static type checking at
> the same time as allowing for extremely flexible typing. For those of you
> not familiar with this, a nice example of the value provided by generics in
> Java is the Collection class and its various extensions providing all kinds
> of lists, maps, sets and so on. So assuming one has a class used for
> aggregating data called Data, then one can create a hashtable-based map of
> (say) string keys to instances of Data as:
>
> var myDataMap = new HashMap<String,Data>();
>
> and then insert instances of Data into it with
>
> myDataMap.put("foo", dataInstance1);
> myDataMap.put("bar", dataInstance2);
>
> and later fetch the values
>
> var diFoo = myDataMap.get("foo")
>
> By now anybody who is listening is probably sick of hearing me bang on
> about hash tables, but they really are quite useful...
>
> Anyway, back to Algol 68 - I think the suggestion of restricted modals,
> summarized by Jose and further elucidated by Paul Leyland, could give us a
> way into this kind of flexibility.
>
> For reference, Jose illustrated the concept with the following procedure
> declaration:
>
> pub proc qsort = (mode X, ref[]X list, proc(ref X, ref X)int cmp) void
>
> And so I assume that we could define a module containing a collection of
> procedures like
>
> pub proc put = (mode X, string k, X v) void
> pub proc get = (mode X, string k) X
>
> I believe the "get" procedure is not feasible however because I think X is
> only known inside the collateral clause parameter list... though I suppose
> it must be also known inside the procedure body {how does that
> work?}... anyway perhaps get would have to look like
>
> pub proc get = mode X, string k, ref X v) void
>
> This all seems plausible to me, since I feel that very many uses of this
> restricted modal concept would want to treat the parametrized mode as
> something quite opaque; or, if not opaque, then the caller would have to
> provide one or more "helper" procedures that could provide access to the
> bits that must be available within the generic procedure. The quicksort
> procedure example of Jose's takes this approach, using a helper procedure
> to carry out the comparison of the keys of two items on the list.
Right.
In Lindsey's restricted modal proposal, the body of the generic routine
can only operate on values of mode ref* X.
> Of course if we're really going to have a useful hash table facility, we
> have to have generic keys as well as generic values, so that we can
> accomodate int, string, Date... whatever keys. So I guess those put and
> get procedures would look like
>
> pub proc put = (mode K, V, K k, V v) void
> pub proc get = (mode K, V, K k) V
> or
> pub proc get = (mode K, V, K k, ref V v) void
>
> Returning to the "what is the scope of the modals" question for a minute, I
> have toyed with the idea that we might want the get procedure to be more
> like
>
> pub proc get = (mode K, V, K k) ref V
>
> so that we can use it on the left-hand side of the assignment, that is,
>
> get("foo",my_data_map) := data_instance_1
>
> thus eliminating the need for a separate put procedure.
Thats a nice idea. Reminds me of the -> operators I got in place for
JSON values, that allow syou to:
JSON_Val cmd := json_new_obj;
cmd->"source" := json_escape_string(src);
cmd->"base" := style;
cmd->"useSpaces" := use_spaces;
cmd->"tabWidth" := tab_width;
string answer = json_str(res->"answer");
> Anyway! I think this idea deserves a lot of consideration as I believe it
> works closely with existing Algol 68 concepts (mode declarations and static
> type checking) and makes a huge jump to extensible code that united modes
> do not come close to providing.
>
> Which brings me to my other three topics: "self", introspection and "mode
> Anything".
>
> In reverse order - I claim that modals by themselves do not solve problems
> like InType and OutType. The problem that arises immediately is that
> operators STRAIGHTIN and STRAIGHTOUT conceal something very much like
> introspection - they have to be able to look inside of structures in order
> "flatten" the structure into rows of SIMPLIN and SIMPLOUT components.
>
> Maybe we would like to use some kind of "compiler writing magic" to
> implement STRAIGHTIN and STRAIGHTOUT. For example, I suppose that every
> time a struct or mode is declared, a hidden pair of STRAIGHTIN and
> STRAIGHTOUT operators could be declared as part of the structure or mode
> that could be accessed by any programmer whose magic wand is sufficiently
> shiny. Maybe that wouldn't be too bad. This could also ensure that no
> operators are created for structures that violate the restrictions
> mentioned in the RR "a mode which does not contain 'flexible', 'reference
> to', 'procedure' or 'union of'.
>
> However, I find it kind of odd that the only place we seem to have
> identified the need for these two operators is in transput... can that be
> right? So just in case, maybe what should really be generated by the
> compiler is the introspection data necessary to implement STRAIGHTIN and
> STRAIGHTOUT.
I agree straightening is best implemented ad-hoc in the compiler.
That said, IMO straightening is specifically tailored to transput, and
probably nothing else. It is just marshalling/demashalling built in the
compiler.
As for Anyode, we know that the Interactive Algol 68 compiler from
Cambridge Oxford Compilers used to support it, as an union with infinite
moods, the set of all possible Algol68 modes. One aspect I like of
Algol 68 unions, along with uniting, is that it allows the programmer to
adjust the amount of desired polymorphism and therefore the associated
run-time overhead: if x is definitely an int, there are no run-time
checks, but if it may be also a real, the value should be tagged and
pertinent run-time checks performed before accessing it.
Let us not underestimate unions: the whole orthogonal syntactic frame is
oriented towards them, including balancing and operator overloading.
After all, a firm syntactic position is one in which the a posteriori
mode of the coercee can be one among _several_ modes, as opposed to a
strong syntactic position, in which the a posteriori mode of the coercee
is exactly _one_ particular mode. In both cases, the desired mode is
known at compile time: in firm positions, the desired mode is resolved
statically by balancing all the modes implied by all the available
versions of the operator.
Adding an AnyMode union to GCC is perfectly feasible.
> Leading to my final topic - "self". In case anyone is still reading and is
> unaware of what I mean by "self", this would be the ability to declare a
> structure containing one or more procedures which could operate on the
> fields of the structure directly. A simple example (not claiming correct
> pseudo-syntax here);
>
> mode Circle = (real x, y, r, proc real area);
>
> Circle c = (3.7, 4.1, 18.0, proc = real: pi * r of self * r of self)
>
> or maybe even
>
> proc circleArea = real: pi * r of self * r of self;
>
> Circle c1 = (3.7, 4.1, 18.0, circleArea);
> Circle c2 = (0.9, 1.3, 12.0, circleArea);
>
> We could think of achieving this as a kind of short hand for something like
> this (which clearly won't work as is but anyway to get the idea across):
>
> proc circleArea = (Circle self) real: pi * r of self * r of self;
>
> mode Circle = (real x, y, r, proc (Circle) real area);
>
> Circle c1 = (3.7, 4.1, 18.0, circleArea(c1));
> Circle c2 = (0.9, 1.3, 12.0, circleArea(c2));
>
> (of course that can't work as written since c1 is not yet available to
> participate in its own declaration, nor is c2).
>
> I know that the "self" concept creates all sorts of interesting conundrums
> in object oriented languages where we also may need to have available the
> concept of "parent of self"... not sure that would apply if we were to
> determine how to handle this in Algol 68+.
>
> I believe "self" might encourage us to bind data and behaviour more closely
> into more comprehensive mode definitions, which to me is a good way to
> encourage more maintainable code.
Hehe I absolutely hate this, it breaks like a thousand and a hundred
language rules, or more :P
An object oriented language design I like is the Ada 95 tagged records.
Using a similar approach, we could have:
mode Circle = tagged struct (real x, y, r);
Then any procedure declared in the same range that takes a Circle as
first argument is a method:
proc area = (Circle c) real: pi * r'c * r'c;
Declaring the method adds an implicit field to the Circle struct:
proc real area;
That you can access via selection, like any other field:
area'c
The tagged struct would add a dynamic tag to values of mode Circle, like
tagged records in Ada 95, that would also allow inheritance, etc.
But if we didn't want to go so far, we could go with something like this
instead:
mode Circle = struct (real x, y, r);
method area = (Circle c) real: pi * r'c * r'c;
I like the tagged approach best though: it is more powerful, and it
doesn't require adding "methods" to the set of declarable objects..
> Ok, that's it!
As always, thanks for the awesome food for thought.
You are the best :)
More information about the Algol68
mailing list