So, in ye olden days, programs were made by directly executing commands on processors, in the beginning using punch cards. The punch cards were how programs were stored. We don’t generally call this a programming paradigm because paradigms are generally useful today and punch cards absolutely aren’t.
Eventually it became possible to store your program into various forms of memory. Soon after this came the first assemblers. Assembly language made a few things about instructing processors to do things easier than just brute force entering every painstaking detail into a keyboard or onto a punch card, most notably having to remember important data addresses, or the operations being used. What limits assembly language programming is the fact that everything being executed is done so directly by the processor.
Soon disk-based systems that could persist information to disk were invented. With this capability came the possibility to store instructions on disk and to refer to those instructions, what we today call libraries. Programming consisted of writing statements that would execute other statements that would eventually turn into machine code or assembly instructions. This is called imperative programming. You can write your code in languages that increasingly approached English.
Well that worked for awhile until it became clear that control flow abstractions were really important. Good control flow makes a huge difference, a big enough one that we have a different word for languages that provide them. This word is procedural, a related word is structured programming. Many languages started out as imperative languages and acquired procedural aspects over time, so today you generally see the two terms used interchangeably.
Well, in order to make procedural programming nice for people to use, it became necessary to keep adding concepts like scope and variables and different kinds of branching. Eventually working effectively with data structures became important and this is how the first object systems arose. This bundles all these disparate concepts under one single semantic idea, a discrete unit of data / behavior. From objects came classes of objects and so object oriented programming was born.
The procedural languages didn’t just become obsessed with objects, around this time they also started getting influenced by mathematical understandings of computation, specifically the lambda calculus, and this focus on functions became known as functional programming. This is where the distinction between procedural and structured becomes important. Functional is structured without really being imperative / procedural. Execution flow is defined according to the mathematical rules of lambda calculus, and “side effects” are treated as the sin to purge from the system.
Related is logic programming, closely related to declarative programming. While imperative programming tells the computer how to do a thing, declarative programming simply specifies what must be done, leaving it up to the mathematical model or prior programming in the computer to decide how it’s done.
The big limiting factor in this era of programming was the speed of the processor. Early interpreted languages like BASIC gave way to compiled languages like C, as it wasn’t yet known how best to make a fast interpreted language. This limitation started going away in the 80s as PCs got more and more powerful. Compilers are very dumb programs, they require extreme specification of what kinds of data they’ll accept. To this end they made type checkers a necessary part of their design.
But now that a compile step wasn’t entirely necessary, we now were able to have dynamic type systems. Sometimes this was done for convenience and speed, as was for Javascript, and other times it was done for power. Lisp, a language that had been around since the 70s, finally started to shine due to machines finally being able to support fast implementations. Lisp is a programming paradigm all on it’s own, built on the idea of code as data, programs can see and modify themselves while they’re still running.
This all led to the era of the general purpose programming language, the mythical beast which promised the coder the ability to learn one language and never have to learn another one their whole career unless they wanted to. These languages can support any kind of paradigm you try to throw at it.
To round out this history I need to mention the esoteric languages, whose whole point is being purposefully obtuse so it’s difficult to do anything with them.