DEV Community

Cover image for Finding the Skeleton Buried In The Code
Derek D.
Derek D.

Posted on

Finding the Skeleton Buried In The Code

So, that title is total clickbait and I apologize for that. Reading it you think this is going to be a story of how I uncovered some nasty hacks buried deep within a legacy code base that changed the very fabric of reality itself. That's not what this article is about though. This article is about seeing through language specific syntax and discovering the fundamental skeletal structure of programming constructs like conditionals, loops, and classes.

My initial thoughts for this article came to me early in 2020 while I was writing Go vs Python a Technical Deep Dive. I figured if I wanted any authority on the topic I should be able to write some Go. So I set aside a weekend to go (pun intended) through the official Tour of Go. At the end of the weekend I didn't just know the features, and data types of Go I could write syntactically correct Go from memory. This made me wonder why did my first programming language, Java, take 6 months to learn but Go only took a weekend? My theory is there is a fundamental skeletal structure in the core building blocks of every programming language, and if you can understand those you can teach yourself and programming language in literally a weekend. It's kind of like knowing Latin. When you know what every other language was derived from those languages become easier to learn.

Without further gilding, the lily here is an in-depth examination of programming's core constructs that cuts away language specific syntax and shows the skeletal structure of programming. These are the constructs that will be examined in this article.

  • Code Comments
  • Output
  • Variables
  • Conditionals (if, else if, else)
  • Loops (for, while)
  • Functions
  • Classes/Objects
  • imports

Code Comments

I always start with code comments so I can use them to explain code in later sections.

Code comments are important because it's how you leave notes, explanations, and documentation in source code for yourself and other developers. A code comment always starts with some special character(s). In most languages, a comment starts with //, and everything on that line following // is ignored by the compiler/interpreter. Some languages, like Python use # to start a code comment. Regardless of the character(s) used to start a comment every language has them and they are incredibly useful. Here's an example in Python, Javascript and Go.

Python

# This is a comment.
Enter fullscreen mode Exit fullscreen mode

Javascript & Go

// This is a comment in both javascript and go
Enter fullscreen mode Exit fullscreen mode

There are also multi-line comments. Most languages start a multi-line comment with /** and end them with **/ very similar to opening and closing tags in HTML. Here are examples of multi-line comments in Python, Javascript, and Go.

Python

"""
Python is weird and uses the triple quote for mutli-line comments.
Note there is a triple quote used to close the comment.
"""
Enter fullscreen mode Exit fullscreen mode

Javascript & Go

/**
 * This is a multi-line comment in Javascript and Go
 * Note each line starts with a star but it is not needed as long as the
 * comment exists between the opening and closing tags
 **/
Enter fullscreen mode Exit fullscreen mode

Outputs

Outputs are ultimately what benefit the user. Without output, a program could solve the most difficult problems in the world but the operator would have no idea of what the solution was. Output can be something printed to console, a file written to disk, or technically event something printed on paper. There is always one built into the language though and that's printing to console.

The first program many developers write is Hello Word. Hello World is a simple program that prints the string of characters "Hello World!" to console. Every programming language has some built-in function that handles this for you, all you have to do is pass the string of characters you want to be printed. In Python that function is quite literally print(), Javascript has console.log() while Go blends the 2 with fmt.Println(). Here is Hello World in each of these languages.

Python

print('Hello World!')
Enter fullscreen mode Exit fullscreen mode

Javascript

console.log('Hello World!')
Enter fullscreen mode Exit fullscreen mode

Go

fmt.Println("Hello World!")
Enter fullscreen mode Exit fullscreen mode

In these examples Python's print() is a function while Javascript's log and Go's Println are methods on an object. Objects and methods will be discussed in more depth later.

Variables

Variables are the first building block that can also be a stumbling block. Comments and Outputs are very static. It's easy to understand what they do. Variables change though, it's why they are called variables. There are only 2 things you can do with a variable; assign it a value and reference that value.

Here's an example of assigning a value to a variable.

Python

name = 'Derek'
Enter fullscreen mode Exit fullscreen mode

Javascript

var name = 'Derek'   // A variable accessible throughout the entire program
let name = 'Derek'   // A variable accessible only within the current scope
const name = 'Derek' // A variable whose values can't change known as a constant
Enter fullscreen mode Exit fullscreen mode

Go

var name str = "Derek" // Creates a variable of type str with the value Derek
var name = "Derek"     // Creates a variable that derives the type str from its value Derek
name := "Derek"        // Shorthand for creating a variable that derives its type from its value
Enter fullscreen mode Exit fullscreen mode

Here you see why Python is so popular, there is only one way to assign variables. You don't have to worry about data types or if the variable is accessible globally or not. At this point, it's sufficient to understand an assignment statement has exactly 3 parts. The variable, the operator, and the value. This is still true for statements with expressions in them like a = 2 + 2. For these kinds of statements, the expression is evaluated and the result gets assigned to the variable. The expressions that appear on the right-hand side of the operator can get incredibly complex but at the end of the day, the entire expression is evaluated into a single value before the assignment occurs.

Conditionals (if, else if, else)

Conditionals are how your program makes decisions. Conditionals have 2 parts; the condition statement that evaluates to true or false, and the condition body which only executes when the condition statement evaluates to true. Conditionals can be chained using if/else if/else condition statements.

If conditionals are the most common conditional and start every chain of conditionals.

Here's an example of an if statement.

Python

count = 1
if count > 0:
    print('count is positive')
Enter fullscreen mode Exit fullscreen mode

Javascript

const count = 1
if (count > 0) {
    console.log('count is positive')
}
Enter fullscreen mode Exit fullscreen mode

Go

count := 1
if (count > 0) {
    fmt.Println("count is positive")
}
Enter fullscreen mode Exit fullscreen mode

In these examples if count > 0: and if (count > 0) are the condition statement. and the print statements, print('count is positive') etc. are the condition body.

The key syntax differences are Python doesn't surround the expression in () while Javascript and Go do, and Python uses a : to indicate the end of the condition statement and the start of the condition body, while Javascript and Go just wrap the condition body in {}. What's known as the conditional expression count > 0 is the same in all 3 languages though.

To continue a conditional chain an else if conditional can be used after the body of a preceding conditional. The else if conditional will only be evaluated if the condition statement that comes before it evaluated to false.

Here are the previous examples now with else if statements.

Python

count = 10
if count > 5:
    print('count is positive')
elif count < 0:
    print('count is negative')
Enter fullscreen mode Exit fullscreen mode

Javascript

const count = 1
if (count > 0) {
    console.log('count is positive')
} else if (count < 0 ) {
    console.log('count is negative')
}
Enter fullscreen mode Exit fullscreen mode

Go

count := 1
if (count > 0) {
    fmt.Println("count is positive")
} else if (count < 0) {
    fmt.Println("count is negative")
}
Enter fullscreen mode Exit fullscreen mode

The key difference here is Python uses the elif keyword instead of else if. elif doesn't do anything special it's just shorthand for else if.

Else conditionals are used to end conditional chains. An else conditional does not have a conditional statement since it only executes when every preceding if and else if condition statement in the chain evaluates to false. This is why else statements are commonly used to define default behaviors in a program.

Here are the examples with else statements.

Python

count = 10
if count > 5:
    print('count is positive')
elif count < 0:
    print('count is negative')
else:
    print('count is zero')
Enter fullscreen mode Exit fullscreen mode

Javascript

const count = 1
if (count > 0) {
    console.log('count is positive')
} else if (count < 0 ) {
    console.log('count is negative')
} else {
    console.log('count is zero')
}
Enter fullscreen mode Exit fullscreen mode

Go

count := 1
if (count > 0) {
    fmt.Println("count is positive")
} else if (count < 0) {
    fmt.Println("count is negative")
} else {
    fmt.Println("count is zero")
}
Enter fullscreen mode Exit fullscreen mode

Loops

Loops are where the languages start to diverge a little bit, mostly because there are types of loops available in some languages and not others, however, those loops that are available in multiple languages generally have similar syntax.

Loops are used to loop over and execute the same code until a condition is true. The main loops are for and while loops which we will start with.

The anatomy of while loops are similar to conditionals. There is a condition statement that evaluates to true or false and immediately following that is the loop body which is executed every time the loops condition statement evaluates to true.

Here is an example of a while loop that will search for the letter a in a jumbled up alphabet. Note Go only has for loops so there is no code snippet for while loops in Go.

python

needle = 'a'
haystack = 'rsthidlmjpqknefgabczowxyuv'
curr = ''
index = 0
while curr != needle:
    curr = haystack[index]
    index += 1

print('found needle at index', index)
Enter fullscreen mode Exit fullscreen mode

Javascript

needle = 'a'
haystack = 'rsthidlmjpqknefgabczowxyuv'
var curr = ''
index = 0
while (curr != needle) {
    curr = haystack[index]
    index += 1
}

console.log('found needle at index', index)
Enter fullscreen mode Exit fullscreen mode

The same differences from conditionals apply here as well. Python separates the loop condition statement with : while Javascript wraps its condition statement in () and the body in {}.

For loops are tend to be more verbose than while loops and can come in 2 forms. The classic for loop and a for each/for in loop.

A classic for loop has 2 parts to it the for statement and the loop body. Subsequently, the for statement is made up of 3 parts, which I call the initializer, the condition, and the step. The initializer sets up a variable used to keep track of the number of
iterations, the condition determines if the loop body should be executed again, and the step which moves the variable created in the initializer to its next step.

Here is an example of a classic for loop in Javascript and Go since Python only has for in loops.

Javascript

count = 0
for (var i = 0; i < 10; i += 1) {
    count += 1
}

console.log('count is', count)
Enter fullscreen mode Exit fullscreen mode

Go

count := 0
for i := 0; i < 10; i += 1 {
    count += 1
}

fmt.Println("count is", count)
Enter fullscreen mode Exit fullscreen mode

In these examples var i = 0 and i := 0 are the initializer, i < 10 is the condition, and i += 1 is the step. Everything between the {} is the loop body. Both of these programs will print out the value 10.

For each and for in loops are used to iterate over every element in a collection. They have 2 parts as well the for in/for each statement and the loop body. Just like a classic for loop for each/for in statements can be broken up into parts as well. These parts are the iteration variable and the collection. The collection is just as it sounds. It's a collection of values that will be iterated over. The iteration variable is a variable that holds the current value being looked at in the collection.

Python

numbers = [1, -1, 10, -5, 8, 9, -4]
positives = 0
negatives = 0
for n in numbers:
    if n >= 0:
        positives += 1
    else:
        negatives += 1

print('Negatives', negatives, 'positives', positives)
Enter fullscreen mode Exit fullscreen mode

Javascript

const numbers = [1, -1, 10, -5, 8, 9, -4]
let positives = 0
let negatives = 0
for (const n in numbers) {
    if (n >= 0) {
        positives += 1
    } else {
        negatives += 1
    }
} 
Enter fullscreen mode Exit fullscreen mode

Go

numbers := []int{1, -1, 10, -5, 8, 9, -4}
positives := 0
negatives := 0
for _, n := range numbers {
    if n >= 0 {
        positives += 1
    } else {
        negatives += 1
    }
}

fmt.Println("Negatives", negatives, "Positives", positives)
Enter fullscreen mode Exit fullscreen mode

In these examples, there is some magic performed by the language in that each iteration of the loop the next value in the collection is stored in the iteration variable. In this case n is the iteration variable and [1, -1, 10, -5, 8, 9, -4] is the collection. This is usually a mind-bending concept the first time you see it so for those of you just getting started. on the first iteration of the loop n is equal to 1 and the second iteration n is equal to -1.

Functions

Functions are very similar to algebraic functions. They receive input(s) and produce an output. Consider the following algebraic function that doubles the value of x.

f(x) = x * 2
f(2) = 2 * 2
f(10) = 10 * 2
Enter fullscreen mode Exit fullscreen mode

f(x) could be called the function declaration (i.e. what the function does) f(2) calls the function with the input 2, which results in the output 4 f(10) calls the function with the input 10, which results in the output 20

This mathematical concept of doubling an input can be expressed in Python like so.

def double(x):
    return x * 2
Enter fullscreen mode Exit fullscreen mode

A function has 2 parts, its signature, and its body. The signature is made up of the function name and the parameters that the function accepts. In some languages, the data types of the parameters and the data type returned by the function are also part of the signature. Here are the declarations for the double function in Javascript and Go since you've already seen it in Python.

Javascript

function double(x) {
    return x * 2
}

// Note this is the same thing just using ES6 arrow functions
double = x => x * 2
Enter fullscreen mode Exit fullscreen mode

Go

func double(x int) int {
    return x * 2
}
Enter fullscreen mode Exit fullscreen mode

def double(x), function double(x) and func double(x int) int are the signature, while return x * 2 is the body. Go is one such language where the data type of the parameters and return value are used in the signature. That's why you see x int for the parameter and int following the closing parens ) for the return type.

The functions are called the same way in all the languages.

Python

double(2) # returns 4
double(10) # returns 20
Enter fullscreen mode Exit fullscreen mode

Javascript

double(2) // returns 4
double(10) // returns 20
Enter fullscreen mode Exit fullscreen mode

Go

double(2) // returns 4
double(10) // returns 20
Enter fullscreen mode Exit fullscreen mode

Classes and Objects

Classes are used to model real-world objects/concepts. Some languages, like Go, have things that are class like while some others, mostly functional programming languages, don't have classes and objects at all.

My favorite example to explain classes is a table. That's right like your run of the mill coffee or dining room table. Classes are complex so I can't say, "here is the format all classes use" as I could for conditionals, loops, and functions. Using tables as an example, however, makes it simple to understand what components make up a class. There are 3 basic components to every class. The constructor, properties, and methods.

The properties are fields, properties, or attributes of the thing you are modeling. In our case, this is a table and those attributes might be height, length, width, height, and legs representing the number of legs a table has.

The constructor is a special method on the class that builds an instance of the class which is an object. Here is an example of a Table class that defines the height, width, length, and legs properties and a constructor for constructing instances of the class. Remember instances of the class are objects.

Python

class Table:
    def __init__(self, height, width, length, legs):
        self.height = height
        self.width = width
        self.length = length
        self.legs = legs
Enter fullscreen mode Exit fullscreen mode

Javascript

class Table {
    constructor(height, width, length, legs) {
        this.height = height
        this.width = width
        this.length = length
        this.legs = legs
    }
}
Enter fullscreen mode Exit fullscreen mode

Go

type Table struct {
    Height int
    Width int
    Length int
    Legs int
}
Enter fullscreen mode Exit fullscreen mode

In these examples, Python and Javascript have explicit constructor methods while Go has an implicit constructor. In Python, the constructor is __init__ and in Javascript constructor. These methods take in parameters that are used to set the properties for the instance of the class being constructed. This is seen with self.height = height in Python and this.height = height in Javascript. Go does the same thing implicitly which is why no constructor needs to be defined in Go. Here is an example of creating 2 tables, myTable and yourTable.

Python

myTable = Table(3, 4, 8, 4)
yourTable = Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

Javascript

myTable = new Table(3, 4, 8, 4)
yourTable = new Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

Go

myTable := Table(3, 4, 8, 4)
yourTable := Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

I like this example because it's easy to see that mytable and yourTable are not the same table even though they have the same height, width, length, and the number of legs. This is an important concept for Classes and objects since they help to model the real world. After all, when I bought my dining room table I didn't buy every instance of that table just because they had the same properties as mine. More importantly, though tables with different properties can be represented using the same Table class. Here's an example of creating a coffee table and dining room table.

Python

coffeeTable = Table(2, 2, 4, 4)
diningRoomTable = Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

Javascript

coffeeTable = new Table(2, 2, 4, 4)
diningRoomTable = new Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

Go

coffeeTable := Table(2, 2, 4, 4)
diningRoomTable := Table(3, 4, 8, 4)
Enter fullscreen mode Exit fullscreen mode

In this case, it's obvious that coffeeTable isn't diningRoomTable because they don't have the same values. An important take away though is regardless of the table represented a table in its general abstract sense can be described as having a height, width, length, and number of legs no matter what those values are. The object's properties can be accessed like so.

Python

print(coffeeTable.height)
Enter fullscreen mode Exit fullscreen mode

Javascript

console.log(coffeeTable.height)
Enter fullscreen mode Exit fullscreen mode

Go

fmt.Println(coffeeTable.Height)
Enter fullscreen mode Exit fullscreen mode

The final components to a class are methods which we briefly talked about with constructors. A method is just a function bound to a class meaning the function cannot be called without referring to the class. Calling a method syntactically is a hybrid between calling a function (i.e. foo()) and accessing an object property (i.e. myTable.height). Here's the code for the rather contrived example of folding tables.

Python

class Table:
    def __init__(self, height, width, length legs):
        self.height = height
        self.width = width
        self.length = length
        self.legs = legs

    def fold(self):
        self.length = self.length / 2

    def unfold(self):
        self.length = self.length * 2
Enter fullscreen mode Exit fullscreen mode

Javascript

class Table {
    constructor(height, width, length legs) {
        this.height = height
        this.width = width
        this.length = length
        this.legs = legs
    }

    function fold() {
        this.length = this.length / 2
    }

    function unfold() {
        this.length = this.length * 2
    }
}
Enter fullscreen mode Exit fullscreen mode

Go

type Table struct {
    Height int
    Width int
    Length int
    Legs int
}

func (t Table) fold() {
    t.length = t.length / 2
}

func (t Table) unfold() {
    t.length = t.length * 2
}
Enter fullscreen mode Exit fullscreen mode

There are 2 main takeaways from these examples. The first is that methods are defined the same way as functions. Functions and methods both use the def keyword in Python, function in Javascript, and func in Go. The difference is they are bound to the class. This is shown very explicitly in Go with the (t Table) in the signature of fold and unfold functions. It quite literally binds those functions to that struct. Python's use of self as the first parameter is a little less explicit but does the same thing. In Javascript, it's completely implicit but it's still happening and you can see that by the use of this. The second takeaway is that self in Python, this in Javascript, and t in Go are references to the object that the method was called from. So for example, if I had 2 variables myTable and yourTable representing 2 coffee tables and fold is called on myTable only the length for myTable will change. Here's the code demonstrating this.

Python

myTable = Table(4, 4, 2, "Wood", 4)
yourTable = Table(4, 4, 2, "Wood", 4)
myTable.fold()
print(myTable.length) # prints 1
print(yourTable.length) # prints 2
Enter fullscreen mode Exit fullscreen mode

Javascript

myTable = Table(4, 4, 2, "Wood", 4)
yourTable = Table(4, 4, 2, "Wood", 4)
myTable.fold()
print(myTable.length) // prints 1
print(yourTable.length) // prints 2
Enter fullscreen mode Exit fullscreen mode

Go

myTable := Table{4, 4, 2, "Wood", 4}
yourTable := Table{4, 4, 2, "Wood", 4}
myTable.fold()
print(myTable.Length) // prints 1
print(yourTable.Length) // prints 2
Enter fullscreen mode Exit fullscreen mode

Imports

Imports come in 2 varieties, full and partial. A full import is the simplest and most common import bringing in an entire module of code. They look like this.

Python

import datetime
Enter fullscreen mode Exit fullscreen mode

Javascript

import moment
Enter fullscreen mode Exit fullscreen mode

go

import "time"
Enter fullscreen mode Exit fullscreen mode

Sometimes you only want a specific variable, function or class from a module and that's when you use a partial import. Go doesn't support partial imports but they look like this in Javascript and Python.

Python

from datetime import timedelta
Enter fullscreen mode Exit fullscreen mode

Javascript

import { timedelta } from moment
Enter fullscreen mode Exit fullscreen mode

Conclusion

This is by no means a comprehensive guide to learning programming. What is hopefully has done though is provided a mental model of programming languages themselves that you can use to teach yourself a new language and/or decipher code written in any language. As always I want to hear from you and if you enjoyed the article I encourage you to follow me on Twitter @d3r3kdrumm0nd and subscribe to the Namespace Podcast that I co-host.

Top comments (0)