I've been experimenting with ways to debug my live programs.
(Kind of a follow-up to archive.org/details/akkartik-mu-2021-05-17, if you squint.)
đ„ debug-coroutine.webm
FWIW - riffing on your unease with globals: guitarvydas.github.io/2023/11/01/Globals-Are-Not-The-Problem.html
đ Globals are not the problem
In this subsection, we explore a significant challenge in software development - the issue of global variables and information structure. Global variables themselves are not the primary problem, but, rather, the problem is the way that globals affect our ability to understand and manage complex programs. This challenge becomes more apparent as programs grow in size and complexity.
FWIW - suggestions re. your box notation: publish.obsidian.md/programmingsimplicity/Suggestions+re+Kartiks+Box+Notation
đ Suggestions re Kartiks Box Notation - Obsidian Publish
re. https://futureofcoding.slack.com/archives/C0120A3L30R/p1698818828191999 add ports to boxes data ports control flow ports one-way arrows show which boxes read the data (globals) show which boxes wâŠ
Thanks for those comments, Paul Tarvydas!
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.
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?
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 bysum
and rendered by the globaldraw
handler of the event loop. draw
doesn't know anything aboutsum
. All it sees is, there's something inResult
, 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" indraw
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".
[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].