I've been experimenting with ways to debug my live...
# two-minute-week
k
I've been experimenting with ways to debug my live programs. (Kind of a follow-up to https://archive.org/details/akkartik-mu-2021-05-17, if you squint.)
g
k
Thanks for those comments, @guitarvydas! I appreciate the support re globals. To make my nervousness really concrete, consider a scenario where I want to run
sum
from the example over 10 numbers. I now have to adjust where I render the results of each call. Which is overhead when debugging a problem. This mirrors the classic problem with globals: functions that use them are no longer reentrant. Locals are robust to arbitrary (scales of) calling patterns. I'm really curious to hear if you have any solutions here.
g
I haven’t wrapped my head around what’s going on behind the scenes in what you’re doing. Can you make the globals into little stacks and display only the topmost value? Lisp 1.5, Forth, S/SL (the compiler language, not SSL) mechanisms work that way - by making the stacks VERY explicit [not helpful: CPUs were not meant to support internal concurrency, hence, only one global stack pointer ; hence, all of the gotchas that accompany faking concurrency on CPUs]. The lesson of Denotational Semantics is to make EVERYTHING, but EVERYTHING, explicit, including a need for reentrancy. I.E. if you need to create variables that don’t get overwritten, create a little stack instead of a single variable. Common Lisp has the concept of “specials”, but that feature gets rarely used. Functions in programming - as we know them - put Locals on the stack (supported by hardware, but, then the O/S needs to swap stack-pointers under-the-hood to avoid clobbering the stack pointers of apps). Does that give you any ideas?
k
Definitely something to think about. Let me explain a little more of what's going on behind the scenes: • I have a global variable called
Result
that gets populated by
sum
and rendered by the global
draw
handler of the event loop. •
draw
doesn't know anything about
sum
. All it sees is, there's something in
Result
, and it draws it at a certain point on screen. • I don't show this in the video, but my hazy plan for larger computations is to add deeper subcomputations into new variables, say
Result_local1
,
Result_local2
, etc. And then define the "visual protocol" in
draw
to say where and how (bar graph, point coordinate, etc.) to render each of them. • But now consider if I want to start rendering n points on screen. I might want the intermediate results in different locations depending on whether I'm computing one or a hundred. Hmm, as I write this out I realize perhaps this isn't an issue with the globals. Any time I reorganize my computation I'm going to have to tweak my visual protocol. If a caller wants to juggle 10 invocations of
sum
, it's the caller's job to save the results to some other new global, with its own visual protocol. Ok, I can live with this! It's a lot like having a database of globals in a web app. It's a minor transform to go from lots of little globals scattered everywhere to a single global table called "intermediate results within subsystem foo".
g
[aside: yeah, that happens to me a lot. So much so, that I’ve come to rely on it. If I have a problem, I try to explain the problem in an email to someone. Usually, by the time I’m done, I don’t have to hit SEND, I understand the problem better and know how to fix it :-].
[aside: this all wasn’t a problem when McCarthy created functional programming. The problems started when lexical scoping was invented to “optimize” away the little stacks that Lisp1.5 caused. Aside to the aside: McCarthy’s version created “structure sharing” by default - you don’t need to clone lists if you follow a truly functional methodology (stacks only, no random access). Further aside: Sector Lisp uses true functional methodology and gets away with a 40-byte GC].