# What makes imperative programming easier or harder to learn?

In my experience, people who have programmed before, whether they're professional or not, almost always consider imperative programming to be obvious. They're usually skeptical that a concept such as variable assignment could be difficult to learn.

On the other hand, in my limited experience (teaching Programming 101, and with people learning to write simple scripts), students who are beginning to learn about programming do find the concept difficult. Given a program like

x := 2;
print(x);
if some_condition() then x := 3;


many students struggle with the concept that print(x) will not print 3 even if some_condition() is true. Writing assignment using = may make the difficulty more widespread.

Is difficulty with imperative programming correlated with certain backgrounds? For example, do students with a strong mathematical background struggle more because they have some prior concept of equality? Do students with more computer time struggle less because they're more used to the concept of a state change (even if they can't formulate it in these terms of course)?

• I had a middle school girl howl: "How can x be equal to x plus 1?" It took me a moment to figure out what she was saying. And, she is correct. (-- all computers around the world instantly stop functioning --)
– user737
Commented Jun 15, 2017 at 17:32
• You are correct that part of the problem is in the notation resembling normal maths. There is nothing wrong with this maths (except that things are much much harder to prove in imperative programs, unless constrained to me functional), it is just different. 1+1 = 1 (Boolean algebra). Commented Jul 10, 2017 at 17:49
• There's two big concepts here that are not fully "common sense". The concept of a variable, and order of operations. These are "operations or steps" not "truths", these are "variables" not "unknown constants". Commented Mar 7, 2018 at 19:56

This is a topic very close to my heart. I think that the skeptical attitude you've alluded to simply comes from the fact that most people were taught imperative programming first (and some were never exposed to any other paradigms at all). Turing's Machine was easier to conceive of as a physical, mechanical device than Church's $\lambda$, and it is presumably by this historical accident that mutability rules the day almost everywhere.

I am not even remotely convinced that imperative programming is more obvious or intuitive than functional programming. In my experience, setting values is a consistent cognitive trap for students, and one that I have to treat with great care. My very first unit for AP is modeled rather loosely after Tom Rogers' phenomenal opening AP unit, Java Ain't Algebra, which takes exactly this issue head on.

In my (by now heavily modified) version of Roger's mini-unit, I make explicit that values can change, spend time tracing them, show why x = 4 + 7 is not the same as x - 4 = 7, and spend a lot of time making clear that the programmer's

=

is only barely related to the mathematical

$=$.

We also begin to explore the runtime stack.

Teachers often miss that this even needs to be covered because, once they are thoroughly inculcated in imperative programming, it can become hard to even see why this is hard in the first place. The variable x was 4, but now it is 5. What could be simpler?

But of course, students exposure to variables prior to CS comes from algebra. And in algebra, when we say that $x + 3 = 7$, we mean that $x = 4$, and it cannot suddenly become $5$. We can solve for values, but we cannot alter them.

Here's the takeaway: if we, as teachers, don't make explicit how very different it is to be able to change values, a certain percentage of students don't ever fully pick up this idea. They'll get hazy bits and pieces of the idea, but have trouble operating, and ultimately fall behind.

In my experience, they don't appear to get noticeably lost until far later in the year, but by then, it is very hard to get them back on track. (When I take on a new tutee in the months leading up to the AP test, this is one of the first areas I check if they seem generally lost.) I have also come to believe that this is one of the key ideas that most CS teachers miss, and is responsible for a substantial portion of computer science's very bimodal results with students.

There is a real cognitive load to considering not the value of x, but the value of x right now, and having to both track that value and figure out when to change it.

In spite of what this all sounds like, I'm not suggesting that functional programming is necessarily easier; it has its own cognitive load, since suddenly we are contemplating many, many simultaneous x's taking place during recursive calls. It's almost as if learning to talk to something as alien as a computer is always going to be a bit tricky :)

• Functional programming doesn't imply recursion. The Bootstrap curricula all use functional programming with only very limited recursion, and get students through a ton of material, all without the problems you rightly identify with imperative programming! Commented Jun 3, 2017 at 0:23
• I was also being a bit flippant in my description of functional programming - my point wasn't specifically about recursion, but rather that it has its own cognitive traps, so it is not some easy panacea for the problems I described about imperative programming
– Ben I.
Commented Jun 3, 2017 at 1:58
• What I mean is that there's an awful lot you can get through without recursion, and you can also do a lot of recursive work through higher-order procedures that "hide" the recursion (as you seem to know). So it's a matter of how your curriculum is designed. Commented Jun 3, 2017 at 11:44
• And yes, there are surely traps in FP too. What's a great pity is that the CS Ed literature has done so startlingly little to identify what they might be, caught up in hysteria about the "paradigm" itself to not investigate it in any real detail. But that doesn't mean it's not a "panacea" (unnecessarily grand term, but I'm just borrowing it from you) for the problems described about imperative programming; I think it is. It just moves some of the problems elsewhere. Commented Jun 3, 2017 at 11:45
• This next September will only be my second year teaching Scheme, so this is an area in which my thinking is very much evolving. Since you are both more experienced in this area, and have clearly thought about it a great deal, I am very curious about what you would think about this question, which I have been wrestling with.
– Ben I.
Commented Jun 3, 2017 at 14:55

The difference may be partially related to whether or not the student has a mental model of the machine (as in simple, possibly physically mechanical, computing hardware), rather than more abstract symbology (algebra).

Changing the bits on a Turing machine tape, or the wheels on a Babbage engine, or the display of a pocket calculator, or the position of the beads on an abacus, as one carries out a sequence of steps, clearly shows how and when state changes, and the visible difference at different steps (program counter values) in the algorithm or program.

Algebraic notation hides the turning of the gears, or the ticking of the (computer's) clock, and thus hides imperative state changes to variable values.

Possibly for this reason, I've seen simplified assembly language (CARDIAC cardboard computer, with 10 instructions and 10 memory locations) taught near the beginning of introductory CS for non-majors courses, before delving into higher level programming language constructs (Lisp/Scheme, et.al.)

• Definitely. If they don't have a mental model, they have nothing of any use. You have to be able to walk before you can build a robot that can walk. This is like the difference between Piaget's Concrete Operations and Formal Operations stages. This change cannot be taught, and not everyone gets beyond Concrete.
– user737
Commented Jun 15, 2017 at 17:54
• In my quest for discovering what really makes mutations so hard I'm leaning towards these exact ideas. It's the hiding of the gears that plays a major role. It probably came to be to "simplify" the material to have the student not deal with "too low" level registers and instructions, but I suspect that a low level intro would make things much clearer. Might be that or might be some nice interface that makes the execution visible. But it's important to picture the execution, not the code, which is what instead I see getting quite a lot of attention. Commented Apr 5, 2019 at 10:08

I think that both the question and the answers to date conflate two things that can be separated: imperative programming and programming with primitive data. These are not the same thing and thinking of them as one leads to some of the difficulties. Programs don't all have to be C-like programs from 1980.

Using modern languages (java, python, ruby, scala...) the instructor can, instead, create a virtual world in which students have their first programming experiences. So, rather than first introducing strings ("hello world") and ints (with their operations - and the limitations of those operations) students can program with "things of interest." While my own preference is to have them learn OOP principles early on, that isn't necessarily implied by having a virtual world.

In the Karel series of books (Karel the Robot, Richard Pattis) and its successors in Java, Python, and Ruby the students first introduction is with a few primitive actions (move, turnLeft, ...) and one kind of primitive "value" (beeper). The world is very rich - provably a Turing Machine.

Using such a world, this one or one of your own devising, you can present the computing model you prefer to students with very little distracting detail (maxInt...). You can then teach program logic and structure separate from that detail.

Once the students have a basic grasp of the "fundamentals" (i.e. not int and string) you can introduce those concepts incrementally. But it is amazing how much you can teach without explicit counting.

One feature of the Karel books that may be a plus or a minus is that, while they give a good foundation in computation, they are not intended to have enough material for a complete course, either at secondary or university level. One person I know is working to build a complete course around Karel J Robot, but I think that is a bit optimistic. But they are good for an introduction, at least: a short course or the first third-half of a regular course.

• This is a point I had not considered, which is strange when I remember that I myself started off in Logo before graduating to BASIC. Thanks for bringing this up. This is an important point.
– Ben I.
Commented Jun 29, 2017 at 12:35
• Uh? I don't mention primitive data types anywhere in my question, and I don't see what this has to do with imperative programming. I guess your point is to teach imperative programming without introducing explicit mutable state, only external actions? That's an interesting idea, but it's rather hard to extract from the irrelevant digression into data types. And your answer doesn't actually say anything about whether this makes it easier to learn assignments. Commented Jun 29, 2017 at 15:51
• I have a funny feeling that this is reminiscent of the "New Math" idea that adults used to roll their eyes about when I was a child. I never found out what New Math was, but I knew that it was bad, very bad. I have trouble imagining what I would teach if I started with a complete abstraction. It would be sort of like a drawing class that teaches you that pencils don't exist. If this can succeed, I surely would like to see that, except that I would have to leave the field, because I don't think I am capable of understanding it.
– user737
Commented Jun 29, 2017 at 17:37

My earlier answer focused on overall program structure, not assignment and mutable state explicitly. The originator was focused otherwise, on assignment.

One of the difficulties for beginners, I think, is that taking a Turing Machine view, or a program stack view, or a mail-box cubby hole view is that it isn't natural for students. Without criticizing that view, let me give a different one that I think is stronger. I don't think of a variable as a "box" into which I can put a value, changing the value later (when allowed). Instead, I think of a variable as just a reference to a value. Using the originators notation, I see

x:=3;


and think that x references a value, 3.

x:=4;


so x references a different variable (not that 3 "becomes" 4) and

x:=x+1;


is just compute a new value using x and then let x refer to the new value;

This, of course is the same reference model we use in other languages (OOP, functional) for things. A variable is just a name (not a box) the name can refer to different values at different times/contexts (Buffy is me here, Buffy is you there). Different names can refer to the same object at the same time (Angel is me and also Buffy is me).

Assignment does nothing more than reassign a name.

Note that I recommend this mental model for beginners, and believe it has substance even for advanced programmers (e.g. me). However, at some point you do want to show the map to machine states, but that is a different issue. If I do that too early, then I ask the students to carry around two different models of computation, one based on logic and another, different one, based on machines. I prefer to keep it simple for the beginner and so try to focus on just the logical (higher level) model. But it takes some work to have a consistent model.

As a general principle, I don't believe that every beginner must recapitulate the entire history of computing in the first course. Said another way, there are lots of things that I know, but it would be counterproductive to tell students. Their path needn't be my path.

Finally, my comments in the earlier answer about separating concerns still holds. A virtual world can free the student from also needing to learn the issues and side effects, etc. of primitive data. Assignment can involve "interesting values" as it does in other computing models.

It just occurred to me that a discussion of Haddock's Eyes can be valuable for making the distinction between a name and the thing to which the name applies (also aliasing, ...). x isn't 3, x is a name that refers to 3. 3 itself is a number. (or if you want to delve deeper - a symbol representing a number...)

• If I follow you, your example about "x:=x+1" would mean that instead of thinking that you are creating an instruction which increments the value of x, you are creating a small function or lambda expression. This might be natural for someone who learned Lisp as their first introduction to programming. It might work for someone used to thinking in a way that is beyond algebra, manipulating expressions as calculus does. For someone who only knows arithmetic, it would be just "poof!", as a former student used to say. If we entirely leave the homeland of programming origins I can't be of much help.
– user737
Commented Jun 29, 2017 at 17:32
• It just occurred to me: what do you call a Lambda function?
– user737
Commented Jul 13, 2017 at 20:09
• Lambda expression = anonymous function. e.g. (x)->{System.out.println("hello " + x);} This one is an example of something called a Consumer in Java 8. It could be an instance of Consumer<String> or Consumer<Integer> or ... Lots of things need not be specified. Commented Jul 13, 2017 at 20:22
• The problem with the box idea is that an assignement x=y doesn't move the object to the other box, but makes a copy. So the analogy is limited. Except if you're talking about unique_ptr in C++ :-) Commented Aug 11, 2017 at 10:54