There Are Three Programming Paradigms (2013)
wiki.c2.com
wiki.c2.com
Imperative programming is programming with time. Functional programming projects time on to space. When you add concurrency, either effectively becomes logic programming: Search runs multiple spatial processes in parallel. Constraint satisfaction treats each constraint variable as its own timeline.
We only don't think of it this way because we've traditionally linearized logic programs. You can linearize search with backtracking and you can linearize constraint satisfaction with a constraint propagation queue.
> Imperative programming is programming with time. Functional programming projects time on to space.
and:
> Constraint satisfaction treats each constraint variable as its own timeline
Monads are the (an?) ultimate exploration of this idea. You project the timeline of the entire world on to a space dimension. Each "bind" operation builds up a new world and you can have many forks of the world, as it's just a model, not the world itself. This model doesn't do anything until you "run" the monad by handing it off to some higher level interpreter.
As for constraint satisfaction, check out Sussman's talk "We Really Don't Know How To Compute" <https://www.youtube.com/watch?v=O3tVctB_VSU> - Even though they implement the propagator networks with a message queue and a single thread, you could in theory run each propagator in parallel, sending messages back and forth.
I don't know what you mean when you say they are also spatial and temporal in nature. You are being extremely glib, and I don't really understand what points you are trying to make.
As a result, I can't see the actual content... I assume the three paradigms were:
1. Imperative
2. Functional
3. Logic-based?
Wikipedia's programming paradigms page has ummm, more than is worth easily counting.
https://en.wikipedia.org/wiki/Programming_paradigm
It also features Homoiconic programming paradigm that so many interviews ask questions about these days.
What? You're telling me that companies discovered Lisp?
It doesn't necessarily mean you see an URL list. It can be implemented by storing the list in Javascript code - and you don't "see" that Javascript code.
The method you mention (mis)uses HTML Tags as datastore for the URLs list.
In short, in this view, programming paradigms are traits of languages, or more precisely a programming paradigm is the particular combination of traits that you include in your language.
A single addition or removal of one language trait can make the language feel quite different.
[1] http://www.info.ucl.ac.be/~pvr/VanRoyChapter.pdf
[2] https://en.wikipedia.org/wiki/File:Programming_paradigms.svg
CTM is a bit more focused on programming language theory, it even has a formal semantics appendix. CTM is highly regarded in Lambda the Ultimate as a basic primer to programming language theory. In contrast SICP is a bit more hacky. It shows you how to build some abstractions, whereas CTM is more about their semantics and how to build programs using different paradigms.
Along with SICP, CTM is easily my favorite programming book. Close seconds are PAIP and TAOP (The Art of Prolog). I wish we could have a Lisp with Mozart semantics and a great ecosystem.
I agree with this, and in this is why I, to some degree, disagree with the idea of a 'polyglot programmer', in the sense of an experienced programmer having general "software engineering skills", appreciation of OOP principles etcetc with the idea that they can quickly pick up any language from having a base set of sw skills.
In reality, understanding the specific implementation of many languages (all of which differ), and the nuances of the features of the languages are important. A single trait can make a significant difference the way you should use a language, not to mention differently-evolved community standards/expectations.
> Precisely because I've picked up so many languages, I also know that the hardest part of that is what you point out
This is exactly it. The people I describe have a few similar languages under their belt, and simply extrapolate from this.
I wouldn't, for example, assume a experienced senior java dev could pick up Scala at the same level within a few months, despite the similarities; they simply lack experience with the new traits.
Whether that's good depends on the problem... certainly SQL has been pretty successful.
Constraint based layout engines are such an example. Business rules engines are another. It's the fuzzy dividing line where we start considering programming techniques to be AI instead of conventional. This line is somewhat arbitrary based on history.
thank you, that seems to characterize it nicely.
A great example of transferring between problem domain language and implementation language is in game scripting. The game might have internal stuff for allocating actors and giving them various attributes, with asset references, state machines, etc. But when you go to script your behavior what you want to work with is "do this sequence of events in order, sometimes pausing, interrupting or branching it." And while you can do this by formalizing a new state machine each time, that's actually too powerful an abstraction to use to populate a script-heavy game full of one-off cutscenes, and many games will therefore go domain-specific and create a special cutscene system that limits the range of programmability.
So what I see goal oriented programming (and related ideas like model-oriented or intentional programming) arguing for is more along the lines of "principle of least power" - instead of wielding the most powerful abstractions directly, you invent a less powerful one to drive them, often compiling from less power into more power as a build step.
This is different in nature from high level abstractions intended to add more general-purpose leverage and user control over the problem definition, which garbage collection, metaprogramming, and formal-proof tools aim for.
There are many problem statements which operate on constant memory or employ a predetermined set of algorithms with known memory usage patterns, and they might employ one or more of these high level leverage tools as an intermediate to compile the definition to running code, without the user model or the runtime model needing them.
But it sounds like something I experienced before. You struggle to try to understand what this "new" thing is that's hot and it doesn't seem new to you at all. It frustrates you as you see other people talking about it excitedly and you feel like you're missing something. In my experience that's all it is. It's not new but a different spin on a long established and understood concept.
On the other hand, I see the level of abstraction as (roughly) the average number of machine instructions executed for every statement or expression in the source language.
With these two definitions, its clear that while "declarative" languages will almost necessarily be at a high-level of abstraction, you can also have languages that operate at a high-level of abstraction without being declarative.
caveat: the original link wouldn't load for me, so I don't know if my response makes sense in light of the original article.
[0]a more technically correct definition is provided by David Barbour https://awelonblue.wordpress.com/2012/01/12/defining-declara...
Prolog is both abstract and declarative. Prolog programs are structured as a set of propositions (i.e. declarations). The difference between Prolog and Bash is greater than the difference between Bash and C, because the nature of Prolog implies a fundamentally different way of designing programs.
Rather than expressing how to take an arbitrary list and rearrange the elements so that they're sorted you express what means for a list to be sorted and let the algorithm figure out how to get there.
* An empty list is sorted.
* A single element is sorted.
* If one splits the list into its first element and its remainder then it is sorted if the remainder is sorted and the first element is less than or equal to the head of the remainder.
This doesn't lead to an efficient sort but it is enough for an algorithm to take any list and produce its sorted form. You can, with the right constraints, get a goal oriented system to carry just about every sorting algorithm and the benefit is that they're really concise and they read like the high level pseudocode you might see in an algorithms class.
It is calling the resolver system though and all the accompanying functions that actually do the sorting.
Not sure what is your definition of "source code", but I'm pretty sure nobody counts external library function implementations as source code for the program. Same as you don't count OS kernel as part of your program's source code.
It's not enough to express the desired result. We would also need to express our preferences for all the other decisions and trade-offs that are made during software development. Do we need this sort to be fast or use as little memory as possible? Synchronous? Does it need an index? Are we optimising for writes or reads? Persistence? What language are we sorting by?
That's for a simple sort. By the time you get into actual problems then it all gets more complex and the trade-offs become something you need to understand before making a decision on. Having an expert to make those decisions is good.
Are there languages that could take this and do merge sort?
* The resulting list must be a permutation of the original list.
- TK Solver [1] Once called "the crooked accountant's spreadsheet", it's a spreadsheet like program where you can change the outputs, and it will try to compute a consistent set of inputs. Good for "what if" problems.
- Kang. This was an early hacking program. It took a set of attacks, and given a starting state (such as "user not logged in") and a goal state ("kernel mode execution") would try to use its tools to reach the desired state.
- Map route finders. Specify start and goal, and a route is generated.
[1] https://www.uts.com/ItemDetails.asp?ItemID=0100-50-0010-00
That capability would be more than just another rung up the abstraction ladder.
Interestingly, the problem of reasoning about programs at that level seems to be pretty nearly "AI-complete". This is true even though it's a formal domain -- one doesn't need, for example, the intuitions about the behavior of physical objects that we humans acquire through years of experience, nor an understanding of human behavior and emotions, etc. etc. We work pretty hard to make sure the behavior of a program is predictable just from understanding the program itself (there are occasional exceptions, of course). Yet even reasoning in such a restricted domain, about even very pedestrian programs, is beyond the state of the AI art at the moment.
The way I think of declarative programming is that it's essentially executable data; not in the manner of lisp, but a description of the problem space (usually as a set of constraints) is translated (compiled, if you will) into an execution plan that satisfies the description.
Declarative programming is the region where you've crossed the (fuzzy) border from eliding the small hows (e.g. memory management) to the big hows (e.g. the order to build components).
At a certain point, enough of a difference in degree changes the way you express a problem or thought, and at that point it's become a difference in kind. A loose aggregate of sand eventually becomes a pile when you've added enough.
It'd be kind of like test-driven or behavior-driven development, except that the programmer only writes the test cases, and the programming environment generates a program that passes the tests.
Imperative programming spells out exactly what happens next; even if it's some high-level abstraction like "findAnswer()", that's still telling us what happens next, and we can jump to the definition of "findAnswer" to see what lower-level step comes next (and so on).
In functional programming, we're still specifying "what to do next", but we're allowed to give a set of things to do; the language accumulates these tasks, and is free to perform them in any order, or even concurrently; the result will be the same regardless (due to confluence). For example in an expression like 'f(g(x), h(y), [a(b), c(d), e])' we're telling the system exactly what to do next, although it's free to perform these function calls in any order it wants (many real implementations choose to define a particular evaluation order, to make e.g. reasoning about performance easier).
In both of these paradigms, the solution to our problem is left implicit: we indicate a solution by the lack of next-steps.
In logic/constraint/goal-driven programming the answer to "what happens next?" is undefined; we haven't told the system what to do next, so it's undetermined. Instead we've told the system when to stop: we make the solution explicit and the next-step implicit. The runtime system has to guess what to try, so it shuffles symbols around and around, stopping if it stumbles upon anything we've designated as a solution. Again, real implementations do define their evaluation order more explicitly for the sake of performance (e.g. depth-first search for Prolog).
This paradigm does not have a fundamental reduction nature (the opposite, actually), is not concerned with memory cell manipulation, and is not limited to predicate calculus (though its products can certainly be used for such).
Well known examples include C++ templates, Scala macros, JavaScript/Perl/Ruby evaluation of text blocks, Aspect Oriented Programming[1], amongst others which I am sure I have unwittingly omitted.
0 - https://www.amazon.com/Generative-Programming-Methods-Tools-...
1 - https://en.wikipedia.org/wiki/Aspect-oriented_programming
AOP, as far as my experience goes, is a structural thing. Again, it doesn't change anything fundamental about how programs are evaluated but rather how the pieces of a program are assembled.
So, it may be a subset of something else or its own paradigm. Worth people thinking on. Plus, I think the style doesn't get enough attention given the results I've seen its practitioners pull off with little code.
I am stressing it to highlight the fallacy of
considering AOP a "programming paradigm".
AOP was not presented as 'a "programming paradigm"'. It serves as a novel example of "Generative Programming", which is the paradigm I mentioned.The reasoning behind including AOP in the examples is based on the ability to use it to produce, manipulate, and/or otherwise enrich program flow independent of the assets being processed. Thus its "generative nature", such as when environments use declarative constructs (such as Java Annotations[0] or C# Attributes[1]) decorating functions/methods/types to manage DBMS transactions.
0 - https://docs.oracle.com/javase/tutorial/java/annotations/bas...
1 - https://msdn.microsoft.com/en-us/library/aa288454(v=vs.71).a...
* Plain text.
* HTML.
* Gratuitous javascript.
FunctionalProgramming
* where the value of an expression is computed, usually close to the lambda calculus.
* or to put it differently: where the fundamental operation is reduction (of applicative terms).
ImperativeProgramming
* where cells in some sort of memory are filled and overwritten with values. Inspired by the TuringMachine and curiously never mentioned in the above table (or to put it differently: where the fundamental operation is assignment.)
* Actually, this generalizes to the fundamental operation being communication. The common case is simply communication to a cell maintained by some memory service (you send either a 'set' or a 'get', or possibly an 'apply-function' as needed for atomic ops or fast XOR processing). The more general case can consist of sends and receives on a fully networked, distributed model.
* * (different author replying) Actually, this "generalization" sound much more like the ActorsModel, which is no where near ImperativeProgramming, actually the actor model is much more similar to FunctionalProgramming than it is to imperative programming.
LogicProgramming (and ConstraintProgramming)
* where a solution to a set of logic formulas is sought; very declarative and incorporating some sort of search strategy like backtracking.
* or to put it differently: where the fundamental operation is satisfaction of a predicate.
* ConstraintProgramming envelopes LogicProgramming, since any logic domain can be expressed in terms of a constraint system, but the inverse is not often easy to express (due to the more strictly typed variables in LogicProgramming). However, the fields are disparate enough to have been split into LogicProgramming, ConstraintProgramming, and ConstraintLogicProgramming. (ConstraintAndLogicProgramming). This sort of programming is also called 'DeclarativeProgramming', but the word 'declarative' is somewhat overloaded.
That's it! Everything else is built on one of these three paradigms (Functional, Imperative and Logic), while sometimes incorporating elements of the others.
If I'd known Ward wanted something more modern I would've volunteered to do a rewrite for something that was faster myself.
That would allow for a return to cachability for archive.org.
Apparently it's to make links clientside in the text. https://github.com/WardCunningham/remodeling/blob/master/sta...
Software like Cucumber [0] might be part of that particular paradigm once the missing pieces around AI/ML take proper form.
Trouble Encountered http://c2.com/wiki/remodel/pages/ThereAreExactlyThreeParadig... can't fetch document
See github link: https://github.com/WardCunningham/remodeling/issues/2
HTH
0 - http://lambda-the-ultimate.org/node/4370
After reading some of Greg Egan's hard science fiction that explores the consequences of a universe where time is a spacelike dimension or another where there are two timelike dimensions, I wondered if programming paradigms could be categorized according to what kind of world lines data or variables may have.
For instance, in some mathematical spacetimes, the future looping back on the past is not a construct you can create. In others (such as Egan's Orthogonal universe), it is quite possible for world lines to arbitrarily loop back on the past. This could be analogous to in a programming paradigm whether statements not yet reached can affect the statement you're looking at. For instance, whether a logic program supports constraint satisfaction.
Mutability and immutability could be analogous to various ways of resolving the Grandfather Paradox. If the result is the timeline is changed/overwritten, that is analogous to a mutable programming paradigm. If the result is that you either are prevented from doing it or doing it results in spawning a duplicate timeline where history is changed, then that is analogous to an immutable or functional paradigm.
Finally, it should be noted that the program and it's entire execution state-space could be regarded as a "four dimensional object" the same way you could consider the universe + its history as a four dimensional mathematical object. (Not really "4" because the space of the program isn't necessarily 3 dimensional, but using the term as a metaphor.) In this sense, you could see the actual execution i.e. the implementation of the execution of the program as a vector or walk through this time-state-space that does not necessarily have to follow the (human) conceptual vector of time through the program! (Not to mention the lexical order of the program text.) For instance, a SQL engine might build a query plan that approaches the execution of a SQL "program's" time-state-space in a much different direction and order than what a human would call the "time vector" through the program.
In Egan's Orthogonal universe he deals with the question of how can living beings experience local time when a universe has all 4 dimensions as spacelike by making a distinction between the arrow of time defined by entropy versus "timelike" dimensions. In our universe the entropy arrow of time usually aligns with the timelike dimension, but in the Orthogonal universe the entropy arrow of time could point along any dimension, but which dimension is the "time" dimension is set by the combined entropy of the local surroundings. Similarly, in a programming paradigm you can choose to define the "time dimension" in many different ways, but there is also an entropy arrow of time.
As an example, imagine a simple imperative programming language that conceptually executes from top to bottom. You could, in principle, make a bizarre implementation that executes backwards, starting with the last instruction and all possible result states, and searches for precondition states that could have caused that result. Repeat for the second to last statement for all of the candidate results, and so forth, until we reach the most initial state.
It's clear that this is possible to implement if the language is restricted enough (for instance if it has few states, or if the only allowed operations are of a certain type). It's also clear that the big-O time/space complexity of this implementation strategy is astronomical for most complex programs. But it is instructive to look at why this is: it is because this time-reversed execution implementation strategy is going backwards against the entropy arrow of time! This is reflected in the enormous amount of energy it would require to compute (drawing on the entropy of an outside system's energy source to reverse local entropy), and/or the gargantuan amount storage it would require (increasing the entropy in space of an outside system in order to reverse local entropy).
Yet at the same time, no one would bat an eye at certain classes of "programs" "running backwards", such as a linear equation solver, or a layout engine, or a logic constraint solver, etc. (One wonders whether a nominally imperative assembly language for a machine based on reversible computing would also fall into this category.)
What I'm getting at is that there are myriad "potentially timelike" dimensions in programs: lexical order, call stack, real execution time, the programmer's conception of how time flows in the conceptual program, etc. And there's also an entropy arrow of time related to how hard/easy it is to reorder the operations. Finally, I view the execution implementation as sweeping a hyperplane (or hypermanifold) through the four dimensional time-state-space of the program & the result of its execution. Depending on the ways that the time-state-space of the program are connected (or connectible) determines along which direction(s) that sweep may or must proceed.
Programming paradigms may be understandable as rules governing how this time-state-space may be connected up, which in turn affects what the entropy arrow of time looks like in execution-implementation-space.
I believe the wiki was recently rearchitected, which would explain why it doesn't scale any more :P.
Just a bunch of incoherent CS speak IMHO.
Reading it without this background may seem like the writings of a ranting crazy person. With the federated wiki remodel, this history was flattened.
The "Actually" part is likely a different author. And another author after the {hr}. And the bullet points under the missing padaradgim are other authors. Then another author for the hr block, and another author after the next one, and then yet another author at "I don't know..." and another author italicizing, and then one that signed as top and... so on and so on.
If you look at an archive.org of the old site - http://web.archive.org/web/20160709091504/http://c2.com/cgi/... for example, at the bottom the edit date is a link. That took one to the edit history showing the IP address that made the change and the diff. It was also something that was robots.txt'ed and so isn't in archive.org.
"CS speak"? Having a formal education in CS should not be derided. Algorithms, data structures, and other components of computer science are important, and programmers lacking a knowledge of these areas can't really be trusted to work in the more demanding corners of our industry. (Many interviewers select based on these criteria.)