💾 Archived View for idiomdrottning.org › history-of-call-tables captured on 2024-05-10 at 11:24:11. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2024-02-05)
-=-=-=-=-=-=-
I use call-tables all the time in my code. Sometimes a good solution is to have two call-tables with the same data, one going from A to B and the other going from B to A. That way I can find anything from anything.
Here’s where they came from!
I was a li’l disappointed in Software Design for Flexibility since I think SICP is the most important philosophical text of the 20th century (sorry Wittgenstein and Sartre, but Eva Lu Ator is where it's at).
SDfF is actually a good book and I can recommend it, just, it’s a normal good book whereas SICP is like the heavens parted. I guess part of the problem is me.
Georg Lichtenberg once said “A book is a mirror. When a monkey looks in, no apostle looks out”. And when I read SICP I had a lot to learn but when I read SDfF twenty years later I had stuff pretty much figured out and what was in it didn’t felt like super new ideas to me and some of them were stuff that felt completely unworkable since they’d require going over all imported functions and recording their arity and things like that, only to get slightly shorter, slightly less readable code.
But as I implemented that arity recorder, I realized that I could extract the body out to a more general create-arity such that:
(define arity (create-arity)) (arity cons 2); => 2 (arity cons); => 2
I thought arity might not be the only thing I’d wanna create with that thing. Me and jcowan had made great hay out of using hash-tables to record nodes in tree.scm. Having a way to bind anything to anything with any equivalence function is awesome!
So I renamed it call-table and shipped it!
Any time you have nodes or vertices of any kind, spray on some call-tables and boom, now you have edges. It’s a more flexible design than what was in Software Design for Flexibility.♥︎
The ability to open the hood and access the underlying hash-table was there from day one. Astractions with escape hatches for the win. Just like how Lisp has lists that you can do list processing on but you can easily access the underlying cons cells.
Chicken already had callable-data-structures which I vaguely remembered the day after making call-tables. I feel doubly guilty to Mario for not only had I ended up biting his schtick with these call-tables but also later on ended up calling a module mdg (for "match-define generics"), which was pure coincidence. I'm sorry, Mario!
When I looked at that again, I saw that they exactly the same api for accessing the data in the hash-table and for accessing the underlying hash-table but what’s so clutch with the way SDfF’s arity and other call-tables work is that you can also easily and side-effectively change what’s in the table. It’s not just callable on the reading end, it’s also callable for setting.
Callable-data-structures did have one awesome feature that I hadn’t implemented then: support for using generalized set!. I added that after but I haven’t actually used it myself yet even though I use call-tables all the time.
For a while I was doing a lot of Clojure work and unlike some other lisp, they use tables all the time, although the paradigm there is usually not to use them as an arity-like store but instead use them in a purely functional way, shadowing them rather than changing them, which is cheap on Clojure because of how they’re implemented (so, on Clojure, the shadow can safely share parts of the original table).
That’s not something I have implemented for mine yet. It’d require swapping away the underlying datastructure from SRFI-69 hash-tables to something more custom, that shares data until nodes are changed in which case it splits those nodes specifically and shares the rest.
One thing I did find useful in Clojure though was being able to write out a table and its contents right in the code, so I implemented the call-table literals.
Like I could (define arity (ct cons 2 not 1 reduce 3)) and it’d then be able to know that (arity cons) is 2 and I can still add new stuff to arity like any other call-table.
That had the unfortunate side-effect that for golfing purposes, (ct'a 0) has fewer characters than (call-table). I never use that in real code, it’s less efficient. As I’m writing this history, I guess I easily could implement (ct) as a shortcut for (call-table), leaving call-table around for when you need to use the special settings of it like seed: or update: and such.
The disadvantage of implementing that would be that it’d tempt people to write terser and less explicit code, but the advantage would be that for macros that expand to calls to ct, they could work even if the args were nil.
Software Design for Flexibility
Structure and Interpretation of Computer Programs
a cup of tea zen at DuckDuckGo