In Part 1 we looked at The Programmer. Now, let’s explore The Program.
What is a Program, really? Some simple ways to describe a program could be: instructions, symbolic tasks, structured actions, or default behaviors. But do any of these really answer the question of what a program IS? Surely, describing a program is not the same as expressing what the program actually is. An important question — one that will be visited again later — is, “Is a program a thing at all if it is not executed?” In other words, is it only a program if it can be executed? And why, You may ask, does any of this matter?
Defining the Components
It is important to frame this essay around what our notion of a program should be and what it will act upon (what is running it). A program, for our purposes, is to be executed by a digital electronic computer, as opposed to mechanical or analog electronic computers. This places us into a discrete numerical context versus a continuous one. This will help us to avoid dealing with concepts such as infinity, and will limit our expression of a program to a finite symbolic domain: glyphs from a written language; opcodes; etc, as opposed to complex signal analysis, or gear ratios and cam deflections.
In order to even approach a satisfactory explanation of a program’s existence, a number of aspects must be considered. Having just established our context, let’s now break down the concept of a program into the aspects that we will use to explore its meaning:
- First-hand Symbolic Representation – The original “source code” of the program; uncompiled/untransformed
- Epistemological Context – The programmer’s concept of the program; practically speaking, this is the “belief” of what it will do (distinct from the “truth” of what it does).
- Ontological Context – The program’s specifications; its requirements; generally, a formalized vocabulary used by the programmer, and other programs, to describe and communicate a program’s conceptual function(s)/relationships
- Perception – Both in terms of the program’s view of its universe, as well as the programmer’s view of what the program does when running/completed; its side-effects
Note, the philosophical terms ‘epistemological,’ and ‘ontological‘ can convey an innumerable number of meanings and carry with them a great deal of “baggage.” However, there exist subsets of the generalized philosophical meanings that apply to a technical context of ontologies of digital computation and structured data. These restricted meanings are what this author intends to be understood in this essay. Keep this in mind while reading this. Also note that the greater scope of philosophical definitions is highly relevant when exploring computing topics such as Artificial Intelligence.
Given the four aspects described above, let us now explore each of them in greater detail.
First-hand Symbolic Representation
We will assume for the sake of simplicity that You, the programmer, are writing the program, and will refrain from recursing into additional levels of meta-confusion by considering programs that write programs. Of course, there is a grey area here when talking about compilers, transpilers, domain specific languages, interpreted languages, and so forth. With that said, take the term “first-hand symbolic representation” to mean any program/script originally coded by the programmer, left unaltered.
I’d wager that almost every programmer at one point has stared blankly at the equally blank screen of their chosen text editor, watching the blinking cursor as if it were somehow unimpressed with them, asking in a dry, sarcastic tone, “Well, hot-shot, whaddya gonna do now?” (If not, then I probably have yet another thing to discuss with a therapist.)
It is in this moment that something very interesting happens — at least, it’s supposed to happen. You begin to express some intangible understanding of Your mind-world in a symbolic form that may itself be examined and further modified. If You’ve read the sections above, You will recognize this as a symbolic representation of:
- What You think You know
- What You provably know
- What You intend to accomplish
In essence, this represents a deconstruction of Your singularly real mind-world, and a reconfiguration of these constituent components into something previously non-existent in that same environment. Viewed from this perspective, it is a monumental feat of creation. It can appear as if You are creating something from nothing. But are You truly adding knowledge that did not exist before, or only manipulating existing pieces into something that reinterprets past perceptions into new ones?
Well, no one said that the laws of physics apply to the mind (not entirely true, but I’m trying to make a point here). Let’s say that both can be true.
The result of Your program can both add knowledge and facilitate a reinterpretation of perceptions.
From a philosophical point of view we should be just fine with this assertion. Also note that the noun above is “the result of Your program.” Hold that thought. Let’s dive a little deeper. A program is a symbolic representation of knowledge, intent, etc. But does this symbolic representation, by itself, contribute to new knowledge or perception? Let’s make another assertion:
A program’s symbolic representation is distinct from the results it produces when executed.
So now we begin to see that our mental model of a program has expanded a little. A program is no longer to be considered a singular entity, but instead is to be taken as a “sum-of-parts,” or a particular configuration of concepts and observations. Can we go even further with this? And to what end?
Spoiler alert: Yes. We will go further in the following sections. As You’ve seen at the top of this section, we have already outlined four aspects of a program. The goal is to walk away with a mental model of a program that allows for a more holistic reasoning about the art of programming itself.
What’s the Point?
I’m glad You asked. The point is this: If we consider the act of creating, and subsequently examining, refactoring, and modifying, a symbolic representation of a program to be distinct in concept from both what we expect it to do, and what it actually does when executed, we can then examine the creation/modification process itself for its effects on the programmer’s knowledge and perceptions. Now we’re getting somewhere.
Unfortunately, at this point I have no links to provide that will aid in demonstrating what I’m trying to convey in this context. The closest analog is that of The Process Theory of Composition. (Read as an imperfect example, not apples-to-apples.) In short, the theory states that the process of writing contributes, if not determines, its product/content. One learns the craft by applying it. (The theory does not perfectly map to our solipsistic view, and would have some serious “holes” when collaboration is restricted to the self.)
Okay, back to this essay. What I’d like for You to consider is that You should view the process of typing out code, hitting the backspace/delete key, and retyping that code as a valuable exercise, an exercise that ultimately defines Your intent and, if actively examined, adds knowledge to Your (the programmer’s) mind-world.
- When learning new stuff, don’t just read it. Type it out for Yourself.
- When doing code exercises, don’t copy/paste examples. Type them out Yourself.
- Don’t rush through code writing. Be editorial. Type out different approaches, comment them out. You can learn a ton by doing something more than one way (especially if You are very familiar with only one of them).
- Regularly read Your old code (1+ month old or more). Can You understand it? What do You know now that You didn’t then?
This is just a fancy way of describing what You think You know about Your program. This is, in essence, Your beliefs and assumptions about a program and the environment in which it will execute. You should notice a theme by now: What You think You know is separate from what You can prove You know (or what is actually happening). When creating a program, it is valuable to reflect upon Your assumptions.
At this point in the creative process, assumptions and perceived knowledge constitute a majority of the driving force behind what You intend to accomplish. That’s absolutely fine and appropriate, but it can’t be the sole driving factor for the entirety of the process.
It’s up to You to reevaluate Your beliefs as You produce more code and as the complexity of Your system increases. Do Your assumptions hold true now that You are able to execute code? Test them. Is there a mismatch between Your assumptions and the way in which You implemented the code to carry out Your will acting upon said assumptions?
In short, You must continually organize Your beliefs and provable knowledge. There are a number of tools You can use to assist You with this process. Here are just a few of the most common:
- Unit Testing – The tests assert the things You believe should hold true about Your program. A caveat: Tests only codify Your beliefs. While failing tests can be an indicator of this, they won’t necessarily tell You if You Yourself are wrong or if the assertion itself is impossible. Dumb example: If You create a test to assert that 1+1 == 3 (because You believe it to be true), the test will always fail. Searching for the bug in the code is a waste of time. Your belief is invalid. The assertion itself is impossible.
- Writing User/System Requirements Docs – It’s very easy to stray onto tangential paths when coding. A tried-and-true technique for organizing Your work is to follow requirements. They don’t need to be fancy. They can be as simple as post-it notes or a text file open on Your desktop. Don’t let process be an excuse to not define/understand what Your software is supposed to do.
- Try to Break It – Sometimes referred to as “fuzzing,” try interacting with Your program in ways You did not originally intend. Provide absurd input values, make random user interactions, turn off Wi-Fi/network connections in the middle of a process, etc. You may find vast new worlds that You didn’t originally envision.
- Work off of a Specification – Take some time to model the components of Your system “on paper” before writing any code. This can be actual pen and paper, or a visual diagramming tool, or just a text file of ideas and how they fit together.
Speaking of specifications, let’s talk about ontologies. Ontology is a complex topic in philosophy. It primarily deals with existence, and reality, and “categories of being.” I can’t begin to give a satisfactory definition of the general abstractions; however, the philosophical ideas of ontology (as a study) have been adapted to a technical context. And this is the ontology to which we refer in this essay (loosely, mind You).
Briefly, when discussing ontology in a technical context, one may be referring to either:
- A standard “vocabulary” or representational scheme used to describe technical components and their interactions/relationships
- A formal specification of a specific system that identifies the actual models and abstractions (and their relationships) implemented by the program at both design and run time.
At first glance, these may appear to be the same thing, and in some instances they actually are (any confusion is perfectly normal). But think of them in this way:
- An accepted/standard WAY to DESCRIBE any technical system (of a particular type/at an organization)
- An instance of an ACTUAL DESCRIPTION of a particular piece of software/hardware (that adheres to an accepted way of describing things)
Hopefully that clears it up a little. It’s all abstractions on top of other abstractions. There are even ontologies that specify ontologies. Yikes! But we don’t really need to go that deep in order to get something useful out of this section.
There are two things I’d like You to consider about the “Ontological Context of a Program”:
- What is Your conceptual *model of the program (and/or the problem it aims to solve / the result You seek)? — This represents Your understanding (aspirations even) of the problem domain. This understanding is a prerequisite to translating it into a symbolic representation (writing the code). Note: Your understanding does not necessarily need to be “complete” or “total,” as long as it is used consistently and is documented accordingly.
- How will You communicate Your concepts to others (or apply Your models to other systems)? — It’s not that there are others to communicate with. It’s more about recognizing that programming has been around for many decades. There are some established, “industry-standard” methods (ontologies) used to reason about and document a program’s components. Learning and adopting these for Your own use can provide useful constructs to aid Your own (and others’) understanding and communication.
*Just for completeness, check out this discussion on the difference between a Model and an Ontology.
Those two considerations, even if taken lightly, have the potential to drastically alter and improve Your own approach to writing software.
I believe that changing Your perspective to that of The Program is a valuable tool that can provide new insights and avenues of thought to explore. Even though we’ve covered only three of the four aspects of the concept of a program listed at the top of this post, it’s still a lot to absorb.
In Part 3 (which will be posted next week), we’ll wrap things up by discussing how The Program perceives its reality.
Tagged with: essay • improvement • philosophy