Mental Model in Programming

A mental model is a representation of some domain that supports understanding, reasoning, and prediction. Mental models permit reasoning about situations not directly experienced. They allow people to mentally simulate the behavior of a system.

Why Mental Models?

  • Can reason about the code and analyze the time and space complexity.
  • Can make coding decisions using the mental model.
  • Can be used to identify the gaps or misunderstandings about current knowledge.

What Happens When You Lack Mental Model

I had lot of difficulty learning CSS. I read many tutorials, courses and even had one-on-one coaching sessions. But something critical was missing. I was not able to make any progress in learning the concepts from any resources.

I bought Jon Duckett’s best selling CSS book. In this book there was a key concept that he explains really well. This provided me the right mental model that I needed to understand all other CSS concepts.

I was not able to come up with a question to clarify this concept even during face to face coaching when the instructor was encouraging me to ask him questions.

Simple and Complex Mental Models

Simpler mental models are sufficient for beginner level coding problems. Simpler mental models break as the difficulty of the coding problem increases.

Complex mental models are required that either adds more details to existing simpler mental models or replace them with complex mental models to solve complex and difficult problems.

What does the movie look like? A program is read from a file stored in an external storage of the computer. This loads the program into the main memory of the computer. The data needed by the program is also loaded into memory from the external storage.

For intance, if you open a word processor by clicking on an icon, it is loaded into memory. If you open your resume, this is the data and it is also loaded into the memory so that the word processor can access and modify it if the user changes it.

The instructions in the word processor program is executed in the CPU and you are able to interact with the user interface. The area in the memory where the program is loaded is called the code segment.

The area in the memory where the static data declared by the programmer is loaded is called the data segment. The stack segment contains the system stack, which is used as temporary storage. The heap segment is a pool of memory used for dynamically allocated memory, such as with malloc() in C.

Let’s consider recursion, which is a difficult topic for beginners to understand. Experts have the mental model to understand recursion deeply and they are able to choose between tail recursion or head recursion based on the problem. They know about the system stack, frames for the procedure calls and how the values of variables are bound to the recursive calls.

The knowledge about the computer memory and how it is used during the program execution can be visualized as shown in the diagram. This is a useful mental model when solving coding problems. Because, we are able to make the design choices.

Computer Memory

Von Neumann Computer Architecture provides a simplified model of a computer for most of the programming tasks.

Design Simulation

As programmers we can visualize the data structures and algorithms to reason, understand and predict the behavior of a program. Expert programmers organize their programming knowledge in sophisticated, flexible and viable mental models.

They organize their representations of code chunks into larger conceptual-programming structures according to the function performed by the code. They form abstract representations based upon the purpose of the code.

Visualizing Data Structure

If you can draw a diagram of the data structure, you have the knowledge about the data structure. We can now translate the diagram into code. We can also manipulate the elements in the data structure and write code if we can visualize the data manipulation in the data structure.

For instance, if you can draw a linked list and understand the concept of nodes and links, you can represent those concepts in code. You can also write code for removing or adding a new node by looking at the before and after pictures of the data structure.

Mental Model of a Variable

Mathematics and Variable

In mathematics, a variable is an unknown which can take on all the numerical values in a predetermined set. In programming languages, it is necessary to specify this concept in more detail because the model for variables depends on the programming paradigm.

Imperative Programming Paradigm and Variable

The classical imperative paradigm uses modifiable variables. According to this model, the variable is seen as sort of container or location that refers to physical memory. We can give a name to the variable and it can contain values. These values can be changed over time, by execution of assignment commands.

Mental Model of a Variable

A programming newbie will have a simple mental model for a variable.

In mathematics, a variable represents a value that is unknown but when such a value is defined the link thus created cannot be modified later.

We can represent a variable with the name x with a box that is filled with a value. It can be seen that the variable (the box) is different from the name x which denotes it.

Variable Representation

Object Oriented Languages and Variable

Some object-oriented languages use a different model. According to this alternative model, a variable is not a container for a value but is a reference to a value which is typically stored in the heap.

Functional Languages and Variable

Pure functional languages use a concept of variable similar to the mathematical one: a variable is nothing more than an identifier that stands for a value. Functional languages do not have any modifiable variables.

Assignment is the basic command that allows the modification of the values associated with modifiable variables. It also modifies the state in imperative languages.

Applying the Mental Model

Expert programmers have the knowledge to understand the meaning behind an innocent looking statement such as an assignment. They have a good grasp of concepts such as the system stack and heap.

They know how to make memory management decisions. They know whether to allocate memory dynamically or static memory is sufficient for a given problem.

Mental Models in Programming: Key to Advanced Problem-Solving and Efficient Coding

Mental models in programming refer to the internal representations of how a system works. They form the foundation upon which we make decisions, interpret data, and understand the concepts. Building robust mental models is vital for mastering programming because they support understanding, reasoning, and prediction about systems or situations not directly experienced.

Importance of Mental Models

Mental models in programming allow us to:

  • Analyze the time and space complexity of code.
  • Make informed coding decisions.
  • Identify gaps or misconceptions in our current understanding.

Consequences of Absence of Mental Models

For instance, while learning CSS, despite reading numerous tutorials, attending courses and coaching sessions, progress may be halted due to a lack of proper mental models. Jon Duckett’s best-selling CSS book offers a critical concept: “Every HTML element gets treated as if it were in a separate box.” This simple mental model forms the basis for understanding many other CSS concepts.

Complexity of Mental Models

Simple mental models suffice for basic coding problems, but complex problems demand intricate mental models that either enhance the simpler ones or replace them. For instance, recursion, a complex concept for beginners, is easier for experts because they possess a deep understanding of system stacks, procedure calls, and variable values during recursive calls. All of this is supported by their mental model of computer memory and how it functions during program execution.

The Von Neumann Computer Architecture offers a simplified model of a computer which is immensely helpful for most programming tasks.

Simulation in Design

Programmers visualize data structures and algorithms to reason, understand, and predict a program’s behavior. Experts structure their code into larger conceptual-programming constructs based on the code’s function. They form abstract representations based on the code’s purpose. This process of simulation aids in rigorous testing of the design and execution plan.

For instance, if you can visualize a linked list and understand the concept of nodes and links, you can represent these concepts in code and modify elements in the data structure.

Understanding Variables through Mental Models

Variables in programming can be understood better through mental models. The interpretation of a variable changes based on the programming paradigm.

  • In mathematics, a variable is an unknown that can take on all numerical values in a predetermined set.

  • In the classical imperative paradigm, a variable is seen as a container that refers to physical memory. It can be named and filled with values that can be changed over time via assignment commands.

  • In object-oriented languages, a variable is a reference to a value typically stored in the heap.

  • In pure functional languages, a variable is an identifier representing a value. There are no modifiable variables in functional languages.

Expert programmers understand the implications of an assignment statement and can make informed decisions about memory management. For instance, they can decide whether to allocate memory dynamically or whether static memory will suffice for a given problem. This understanding becomes crucial in languages like C/C++ where memory management is not hidden from a programmer.

In essence, robust mental models in programming pave the way for advanced problem-solving and efficient coding. They allow us to delve deeper into understanding systems and devise effective solutions.

Absolutely! Here are a few examples to illustrate some of these ideas.

Variables in Different Programming Paradigms

  1. Imperative Programming Paradigm (Python, for instance)

    Here, a variable can be imagined as a box where we can store some value. We can change the value in the box as many times as we want.

    1
    2
    3
    4
    
    x = 10  # We've put '10' in the box named 'x'
    print(x)  # It will output: 10
    x = 20  # We've changed the value in the box to '20'
    print(x)  # It will output: 20
    
  2. Object-Oriented Programming Paradigm (Java, for instance)

    In Java, which is object-oriented, variables of non-primitive types are references to objects in memory.

    1
    2
    
    String str1 = new String("Hello");  // str1 is a reference to a new String object
    String str2 = str1;  // str2 now refers to the same object as str1
    
  3. Functional Programming Paradigm (Haskell, for instance)

    In functional languages like Haskell, a variable, once defined, cannot change its value - just like in mathematics.

    1
    2
    3
    
    let x = 10 in
    let x = 20 in
    x  -- This will give an error because you tried to change 'x'
    

Recursion and Memory Management

Consider a simple recursive function in Python, which finds the factorial of a number.

1
2
3
4
5
def factorial(n):
   if n==0: 
       return 1
   else: 
       return n*factorial(n-1)

In this example, each recursive call to factorial creates a new frame on the system stack, which includes its own copy of n and a return address. After a recursive call completes, its frame is popped from the stack, and control returns to the calling function.

This model helps us understand why large recursion depths can lead to a Stack Overflow error: there’s only a limited amount of space for the stack, and each recursive call consumes a chunk of that space.

Data Structure Manipulation

Consider a singly linked list in Python:

1
2
3
4
5
6
7
8
class Node:
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None

Understanding how a linked list works helps us implement methods to add, remove, or find nodes. For example, to append a new node:

1
2
3
4
5
6
7
8
def append(self, data):
    if not self.head:
        self.head = Node(data)
    else:
        curr_node = self.head
        while curr_node.next:
            curr_node = curr_node.next
        curr_node.next = Node(data)

In this method, we visualize the linked list and traverse it node by node until we find the end, then add our new node. The mental model guides our programming decisions.