Alan Kay on Lisp
quora.com
quora.com
([operator] [argument1] [argument2] [argumentN])
and for Smalltalk as [object] [message]
So far I haven't seen anything like that for languages with the C-like syntax. Don't get me wrong. For example I love Go, but sometimes I miss the beauty of Lisp and Smalltalk.make-range from: 10 to: 20 calss (make-range.from:to 10 20) or something similar. Free nanodsls
smalltalk simplicity lies in that the metamodel is actually human reasoning friendly, even while dynamic, you have a clear picture of what's gonna happen in case of errors etc. It didn't please visual age users, which called smalltalk messagenotunderstoodlang but I believe it was due to the wrong viewpoint.
There's so much power in being able to think about a language (or your application) without having to always dig into the code.
make-range from: 10 to: 20 calss (make-range.from:to 10 20) or something similar. Free nanodsls
For the uninitiated among us, what does that do and what's so magic about it? 1 to: 10.
Sends the "to:" message to the number 1 with the parameter 10. Returns a range representing the numbers from 1 to 10. You can then iterate that range with by sending it the "do:" message with a block argument. 1 to: 10 do:[ :i | <do something with i> ].
And so on. Looks a lot like syntax for a for-loop, but is just message sends to collections with keyword syntax. Every type of collection implements do: (and collect:, select:, reject: etc.) Conditionals work the same way: 1 > 0 ifTrue: [ <do something here> ]
Sends the ifTrue: message to the result of 1 >0. This will be the true object, which is the sole instance of the True class (subclass of Boolean). The True class defines ifTrue: so that it executes the block. Conversely, the False class defines ifTrue: so that it does nothing, ignoring the block.The fact that what is usually syntax is just message sends means that you can define your own messaging protocols in libraries that also look like syntax.
Dan Ingalls explains: https://youtu.be/P2mh92d-T3Y?t=574
Also, the Grace language expands on this concept by allowing effectively regular expressions of these keyword parameters. Really quite nifty.
1 to: 10 do:[ :i | <do something with i> ].
would be a single message `to:do:`, not a chain of messages. (1 to: 10) do: [ :i | Transcript show: i ; cr].
also works in Pharo. Both are valid because:"to:do:" is a message understood by the Number class
"to:" is a message also understood by the Number class that yields an Interval object, which understands the "do:" message.
I didn't say you could not build a message chain to do that, only that to:do: as shown in mpweiher's original comment is a single message ;)
As the other respondent wrote, it still works the same way but with parentheses, and the to:do: exists as a convenience.
In Objective-Smalltalk[1], I added | so you can do this without parenthesis:
(1 to:10) do: [ :i | stdout println:i. ].
can become: 1 to:10 | do: [ :i | stdout println:i ].
This is nice when you are constructing a statement interactively, for example on a command line and don't want to go back. It also makes things less convoluted when the chain gets longer. It also "fixes" the asymmetry that chaining works with unary messages but not with keyword messages.Of course, the whole thing becomes even more elegant, IMHO, with a Higher Order Message (HOM)[2]:
stdout do println: (1 to:10) each.
YMMV.From APIs to languages: generalising method names
https://dl.acm.org/citation.cfm?doid=2936313.2816708
pdf: https://michael.homer.nz/Publications/DLS2015/paper.pdf
morning paper: https://blog.acolyer.org/2015/11/09/from-apis-to-languages-g...
That’s a fairly trivial example but it demonstrates how readability is improved. You can imagine how this becomes even more useful with methods that take many parameters.
An interesting document to look at regarding this is Swift’s guidelines on naming methods.
Is that something like keyword arguments in Lisp?
The method name would be "rangeFrom:to:withStep:" and that means you call it exactly like that, with arguments after the colon.
Objective C is the same.
The syntax you show is Swift, which is a weird mash-up of C/C++ style function call/method call syntax with keywords jammed into them. To me, actual Smalltalk syntax is just so much cleaner:
0 to: 10.
It manages the "reads like english" trick without trying to be english-like or natural-language-like. (See Applescript for the disaster that results from that: "Many of the current problems in AppleScript can be traced to the use of syntax based on natural language" http://www.cs.utexas.edu/~wcook/Drafts/2006/ashopl.pdf).You have to write it in Applescript, but you get to read it only in transliterated Perl.
obj.do_thing("string", with_factor=8, log_to=logger)
obj do_thing: "string" with_factor: 8 log_to: logger
One of those reads as a sentence a little better. But to be fair python learned this from languages that had already taken this idea from smalltalk.Like most older languages smalltalk's implementation is more powerful than the bastardization that other languages learned from it. Library interfaces in smalltalk are built around this syntax feature. Note how the function call looks the same as the keyword argument. This shared syntax makes the language read better. It makes function calls and keywords look like a normal infix language (e.g. the math expressions in most languages). Where parens are used like any other infix language (rather than be overloaded for function calls):
(obj do_thing: "string" with_factor: 8) log_to: logger
would then call ".log_to(logger)" on the result of the parenthesized expression. The same way in (2 + 4) * 5 multiplies the result of the parens by 5. A single syntax structure for all these concepts - rather than mixing multiple syntax - allows it to express new ideas in the same syntax as everything else. Like lisp does.> This shared syntax makes the language read better. It makes function calls and keywords look like a normal infix language (e.g. the math expressions in most languages).
Ironically, since Smalltalk also applies this uniformity to arithmetic operators, they don't behave how most people would expect: a + b * c - d is executed like (((a + b) * c) - d).
And yet, Smalltalk got it wrong by making all the parameter names mandatory, which leads to sources that are much more verbose than they need to be.
The correct approach to this is to make parameter names optional, so they're only used when disambiguation is necessary to clarify the intent of the code.
For example?
They are actually part of the method name.
So
robot put: thing in: container.
And
robot put: thing on: shelf.
Are two separate messages that are both understood by the robot instance - "put: in:" vs "put: on:"
Are you hota_mazi ?
foo(1, 1, create = true)
This is the best of both worlds, allowing you to only name parameters when there can be ambiguity (for example when the function takes two int parameters and both mean very different things).Smalltalk forces you to always name your parameters, which leads to overly verbose source files.
foo(1, 1, create = true)
:is supposed to do.Please show a meaningful example so we can see whether the Smalltalk is overly verbose or your alternative is overly ambiguous.
How about this:
createWindowAtCoordinates(10, 10, redraw = true)
vs createWindowAtCoordinates(x = 10, y = 10, redraw = true)
Regardless of how you feel about this specific example, surely you can agree that it's not always necessary to specify all the parameter by names?However (the specifics always matter) if we're assuming that, and we're bothered about verbosity then why say:
createWindowAtCoordinates(10, 10, redraw = true)
:instead of assuming that it will be understand that the window will be created at a point: createWindowAt(10, 10, redraw = true)
:and something more like a Smalltalk method send: Window new openAt: 10@10 redraw: true
:where we've used the x then y convention, and a binary message @, to create a point (without mandatory parameter names).... and again, you focus on the specific example and completely miss my more general point.
I'll give this one more try: I prefer a language that gives me the option of specifying the parameters names over one that forces me to always specify them.
That's the only way we can see whether what you claim makes sense.
> … which leads to sources that are much more verbose than they need to be.
We can see that for the example you provided, the Smalltalk style need not be "much more verbose" than the alternative you provided.
Using positional arguments will be a little more concise than using keyword arguments, but only a little.
So you could build simple data processing apps with LISP, but nowadays writing a program is not just about algorithms but in fact mostly about utilizing APIs provided by device and service vendors.
When you build an iPhone app with Objective-C, you use the [object] [message] paradigm but it's still complex, and it's not because of the message passing paradigm itself, but because of the complex Cocoa APIs it needs to use.
For example, here are some of the distinct syntax forms in Racket (Scheme):
(+ 3 4) # Procedure call
(lambda (x) (+ x x)) # Lambda expression
# See also case-lambda
(let ((x 23) (y 42)) # Variable binding
(+ x y)) # And also:
# let*, letrec, letrec*, let-values,
# let*-values, let-syntax, letrec-syntax, local
(set! x 4) # Assignment (mutating)
(define x 23) # Defined value
(define (f x) # Defined procedure
(+ x 42))
(quote a) # Quotation
(#%datum . 10) # Quotation (keywords prohibited)
# Conditionals
(if (> 2 3) 'yes 'no)
(cond ((> 3 2) 'greater)
((< 3 2) 'less))
(and a b)
(or a b)
(cond ('(1 2 3) => cadr) # Test clause
(else #f)) # Else clause
# Guarded evaluation
(when (positive? -5)
(display "hi"))
(unless (positive? 5)
(display "hi"))
# Dispatch
(case (+ 7 5)
[(1 2 3) 'small]
[(10 11 12) 'big])
# Sequencing
(define x 0)
(begin (set! x 5)
(+ x 1))
# See also: begin0, begin-for-syntax
# Iterations and Comprehensions
# for, for/and, for/or, for/vector, for/hash, for/hasheq ...
# Modules and imports
(module id module-path form ...)
(require ...)
(provide ...)
As you can see, although the language consistently uses parentheses, it has similar syntax structure to other languages: assignments, definitions, functions, switch/case, etc. Some of the syntax forms above have multiple variations, as well.Every single one of your examples is an S-Expression where the first element of the list is an operator/function followed by N arguments, from an abstract context-less view, anyway.
In lisp there are atoms, lists, and expressions. They have uniform expression but not uniform meaning.
It’s not as if there is just one form `(<op> <arg1> ... <argN>)` where the expression means invoking `op` with some arguments.
A human or machine interpreting the code needs to be aware of these forms and the meaning/semantics of each one. Consider the following S-exps:
(if a b)
(set! a b)
(define a b)
They’re all three-term S-expressions, but you can’t understand their meaning without contextually interpreting the first term. There is a second layer of contextual syntax. Similarly: ((lambda (x y) (list y x)) 1 2)
Consider the term `(x y)` in that expression. Without context, it looks like a call to the procedure `x` with `y` as input. With context, it’s a list of argument names for the lambda function.In languages like C# and Java there is just one meaning of:
f(a, b);
You know this is actually a procedure invocation with some arguments.There are trade offs to each approach. I’m not saying that one is better, just that there’s more to Scheme syntax than S-exps. Concatenative languages like Forth and Factor have even less syntax than Scheme.
Then, of course, there is the specifics of the call. Is it single dispatch? Where is it dispatched? Etc.
Which isn't really to argue against the point. Yes, you need context. Pretty much always. Having gotten used to lisp, I find it much easier to understand. I realize familiarity helps, though.
(let ((x 23)
(y 42))
(+ x y))
will return data as a return and compilation will continue (or stop because some sort of error/exception occurs).The important bit is at compile-time, a function invocation passes the rest of the form to the function as arguments without first evaluating them. Thus there is no syntax for quoting the list of binding forms. At runtime, a function invocation passes the rest of the form to the function as arguments but will evaluate all arguments before doing so. Knowing what is and isn't a macro is a bit tricky in some lisps (and in others there are sigils, reified tables listing the macro symbols, or metadata to let you know what you're dealing with).
The only distinct syntax elements you provided are:
quote - 'small
strings - "hi"
function call - (function arg0 arg1 argN)
dotted pair - (datum . 10)
sharp dispatch - #%datum
Lisps do have syntax. Some provide notations for alternate data types like keywords or fixnums; data structures other than lists like hashes, sets, vectors, etc; or ways of dispatching at read-time. It is worth pointing out that all of the things you mentioned as syntax (which certainly are syntax in most other languages) are merely function calls in lisp.[0] There isn't a significant difference between a macro and a special form here. One is looked up in the application or prelude and is written in lisp, the other is provided by the compiler and possibly not written in lisp.
> A core syntactic form[] is parsed as described for each form in Syntactic Forms. Parsing a core syntactic form typically involves recursive parsing of sub-forms, and may introduce bindings that determine the parsing of sub-forms.
top-level-form = general-top-level-form
| (#%expression expr)
| (module id module-path
(#%plain-module-begin module-level-form ...))
| (begin top-level-form ...)
| (begin-for-syntax top-level-form ...)
general-top-level-form = expr
| (define-values (id ...) expr)
| (define-syntaxes (id ...) expr)
| (#%require raw-require-spec ...)
expr = id
| (#%plain-lambda formals expr ...+)
| (case-lambda (formals expr ...+) ...)
| (if expr expr expr)
| (begin expr ...+)
| (begin0 expr expr ...)
| (let-values ([(id ...) expr] ...)
expr ...+)
| (letrec-values ([(id ...) expr] ...)
expr ...+)
| (set! id expr)
| (quote datum)
| (quote-syntax datum)
| (quote-syntax datum #:local)
| (with-continuation-mark expr expr expr)
| (#%plain-app expr ...+)
| (#%top . id)
| (#%variable-reference id)
| (#%variable-reference (#%top . id))
| (#%variable-reference)
See section 1.2.3.2 which specifies the expansion steps. Note that the expansion model differentiates between handling transformers aka macros and core syntax forms.To be fair, it seems that Scheme (R5RS) considers some of these forms to be "derived expression types" and specifies their definition using `define-syntax` [1]. However, its core syntax forms still include the following [2]:
expression = variable
| literal
| procedure call
| lambda expression
| conditional
| assignment
| derived expression
| macro use
| macro block
... and there's a whole sub-grammar for specifying transformers (macros): <transformer spec> =
(syntax-rules (<identifier>*) <syntax rule>*)
<syntax rule> = (<pattern> <template>)
<pattern> = <pattern identifier>
| (<pattern>*)
| (<pattern>+ . <pattern>)
| (<pattern>* <pattern> <ellipsis>)
| #(<pattern>*)
| #(<pattern>* <pattern> <ellipsis>)
| <pattern datum>
<pattern datum> = <string>
| <character>
| <boolean>
| <number>
<template> = <pattern identifier>
| (<template element>*)
| (<template element>+ . <template>)
| #(<template element>*)
| <template datum>
<template element> = <template>
| <template> <ellipsis>
Being able to write or evaluate Scheme fully requires an understanding of all of these distinct syntax forms.[0] https://docs.racket-lang.org/reference/syntax-model.html#%28... [1] http://www.schemers.org/Documents/Standards/R5RS/HTML/ [2] http://www.schemers.org/Documents/Standards/R5RS/HTML/
Of the above examples you posted: lambda, let, set!, cond, and, or, when, unless, etc, may all be implemented as a library in the Kernel Report. Some of the others aren't in the report but can be trivially implemented as compound operatives. This simplifies the compiler implementation to requiring only a handful of primitives.
A distinguishing feature of Kernel is the lack of quote in the report. Although it has a trivial implementation ($define! $quote ($vau (x) #ignore x)), quote is considered harmful by Shutt due to the way it interacts with operatives. Also omitted from the language is macros, as their capability can be provided by first class operatives at the library level.
The big downside to Kernel is performance. Compilation is generally not possible because the behavior of operatives can depend on runtime defined environments. This would be one of the reasons macros were chosen over fexprs in the 80s. It means Kernel is not a good language for number crunching, so it'd be nice to have an FFI for the cases where we need to invoke optimised code. (Currently no FFI spec exists).
[1] https://web.wpi.edu/Pubs/ETD/Available/etd-090110-124904/unr...
---
First, let me clear up a few misconceptions from the previous answers. One of them said “Try writing an operating system with Lisp”, as though this would be somehow harder. In fact, one of the nicest operating systems ever done was on “The Lisp Machines” (in Zeta-Lisp), the hardware and software following the lead of “The Parc Machines” and Smalltalk (and we in turn had been very influenced by the Lisp model of programming and implementation. (And these operating systems in both Smalltalk and Lisp were both better and easier to write than the standard ones of today.)
Another interesting answer assumed that “the test of time” is somehow a cosmic optimization. But as every biologist knows, Darwinian processes “find fits” to an environment, and if the environment is lacking, then the fits will be lacking. Similarly, if most computer people lack understanding and knowledge, then what they will select will also be lacking. There is abundant evidence today that this is just what has happened.
But neither of these has anything to do with my praise of Lisp (and I did explain what I meant in more detail in “[The Early History of Smalltalk](http://worrydream.com/EarlyHistoryOfSmalltalk/)”).
To start with an analogy, let’s notice that a person who has learned calculus fluently can in many areas out-think the greatest geniuses in history. Scientists after Newton were _qualitatively_ more able than before, etc. My slogan for this is “Point of view is worth 80 IQ points” (you can use “context” or “perspective” etc.). A poor one might subtract 80 IQ points! (See above). A new more powerful one makes some thinking possible that was too difficult before.
One of our many problems with thinking is “cognitive load”: the number of things we can pay attention to at once. The cliche is 7±2, but for many things it is even less. We make progress by making those few things be more powerful.
This is one of the reasons mathematicians like compact notation. The downside is the extra layers of abstraction and new cryptic things to learn — this is the practice part of violin playing — but once you can do this, what you can think about at once has been vastly magnified. There were 20 Maxwell’s Equations in their original form (in terms of partial differentials and cartesian coordinates). Today the four equations we can think about all at once are primarily due to their reformulation by Heaviside to emphasize what is really important about them (and what is likely to be problematic — e.g. the electric and magnetic fields should probably be symmetric with respect to movement, etc).
Modern science is about experiencing phenomena and devising models whose relationships with the phenomena can be “negotiated”. The “negotiation” is necessary because what’s inside our heads, and our representations systems etc have no necessary connection to “[what’s out there?](http://www.vpri.org/pdf/m2003001_human_cond.pdf)”.
Taking this point of view, we can see there can be a “bridge science” and “bridge scientists” because engineers build bridges and these furnish phenomena for scientists to make models of.
Similarly, there can be a “computer science” and “computer scientists” because engineers build hardware and software and these furnish phenomena for scientists to make models of. (In fact, this was a large part of what was meant by “computer science” in the early 60s — and it was an aspiration — still is — not an accomplished fact).
The story behind Lisp is fun (you can read John McCarthy’s account in the first History of Programming Languages). One of the motivations was that he wanted something like “Mathematical Physics” — he called it a “Mathematical Theory of Computation”. Another was that he needed a very general kind of language to make a user interface AI — called “The Advice Taker” — that he had thought up in the late 50s.
He could program — most programs were then in machine code, Fortran existed, and there was a language that had linked lists.
John made something that could do what any programming language could do (relatively easy), but did it in such a way so that it could express the _essence_ of what it was about (this was the math part or the meta part or the modern Maxwell’s Equations part, however you might like to think of it). He partly did this — he says — to show that this way to do things was “neater than a Turing Machine”.
Another observation about this is that the “slope” from the simplest machine structures to the highest level language was the steepest ever — meaning that the journey from recognizable hardware to cosmic expression is a rocket jump!
As is often the case — especially in engineering — a great scientific model is often superior to what exists, and can lead to much better artifacts. This was certainly true here. Steve Russell (later famous for being the main inventor and programmer of “SpaceWar”) looked at what John had done, and said: “That’s a program. If I coded it up we’d have a running version”. As John remarked: “He did, and we did”!
The result was “unlimited programming in an eyeful” (the bottom half of page 13 in the Lisp 1.5 manual). The key was not so much “Lisp” but the kinds of thinking that this kind of representational approach allowed and opened up regarding all kinds of programming language schemes.
A fun thing about it this is that once you’ve grokked it, you can think right away of better programming languages than Lisp, and you can think right away of better ways to write the meta descriptions than John did. This is the “POV = 80 IQ points” part.
But this is like saying that once you’ve seen Newton, it becomes possible to do electrodynamics and relativity. The biggest feat in science was Newton’s!
This is why “Lisp is the greatest!”
Look, I love Lisp as much as the next hacker, but is Alan Kay for real? Where's the Lisp equivalent to this C code?
https://github.com/dwelch67/raspberrypi/tree/master/blinker0...
More importantly, where's the accompanying toolchain? Seriously, I really want to know because I'd love, Love, LOVE to play around with it.
But hardware access - replacing C - is the really easy part. The real power of a lisp/Smalltalk OS is getting away from the hardware in one "rocket jump", as Alan put it. Once you can start using integrated high-level language constructs spanning the whole of the abstraction tower from hardware through user interface, that's when it starts to get fun. The really good stuff is the new abstractions you can unlock. Smalltalk is a far better Unix than Unix, from that point of view.
This is why I think someone is going to build something like an actual top to bottom Lisp/Smalltalk/Whatever machine sooner than we think.
If Smalltalk machines existed, I would buy one today.
Therefore we could have all kinds of exotic, bespoke personal computers again, except that unlike the 80s and 90s we now have fairly standardized formats across systems. This gives us the freedom to make whatever kinds of architectures and OSes we want. In this case, that could even include a machine whose board is designed from the ground up to run Lisp or Smalltalk or something we evolve out of those as its operating system.
The web browser that would come with such a default system would be written in one of those languages. Yes, I know making a web browser is an enormous undertaking, but at least some totally new personal computing system wouldn't have to then also implement office productivity etc.
For graphics you don't need X or Wayland or any of that stuff. BitBlt was able to handle graphics in Smalltalk, because the hardware of the Alto (via microcode) was setup to allow Smalltalk to handle all graphics. If you truly wanted something optimized, something like Nile/Gezira and DSLs written in the environment could work too.
The whole point is that such a system would need to be redesigned and rebuilt from the hardware level on up. A difficult task, to be sure, but the reward is invaluable: the ability to reason about the entire system through the system itself.
In the 80s and 90s when there was a lot of diversity in consumer personal computers, we were all missing a couple of important things: standards and standard interfaces / formats. The web is one such standard. If a system has a web browser, it can do a hell of a lot with other types of computers everywhere. But also notice how today, unlike 10-15 years ago, we don't worry as much about file formats etc. We don't worry as much about which piece of software runs on which platform, in part because of other standards.
All of that frees us up to try more unique personal computing environments again. At minimum, any such system would need to have a web browser as a user level application, since it already gives them access to email, calendar, and basically the rest of the world. In other words, the web browser no encompasses all of this crap that personal computing environments needed to develop in order to have something usable.
Personally I'd want something Smalltalk-like all the way down in the system. Want to inspect TCP/IP packets as they come and go? No problem, they're just Smalltalk objects like everything else in the system. Stuff like that.
Have you ever heard of the STEPS project? It's a complete OS, with applications libraries, and compiler collection, all in 20,000 lines of code. Pretty real if you ask me. Here are the reports (latest first).
http://www.vpri.org/pdf/tr2012001_steps.pdf
http://www.vpri.org/pdf/tr2011004_steps11.pdf
http://www.vpri.org/pdf/tr2010004_steps10.pdf
http://www.vpri.org/pdf/tr2009016_steps09.pdf
What I'm after is the Common Lisp or Scheme equivalent to something like the FreeBSD kernel's libkern, plus a compiler/linker capable of generating an image that could be read into memory by a boot loader:
'One of our many problems with thinking is “cognitive load”: the number of things we can pay attention to at once. The cliche is 7±2, but for many things it is even less. We make progress by making those few things be more powerful.'
just beautiful
Before the advent of cheap writing instruments, and especially cheap printing, we used poetry to almost effortlessly recall and communicate large working sets of data, privately or in discourse. With poetry and similar mnemonic devices people memorized volumes of information. A student or scholar in ancient Greece would have literally memorized all the classics. Someone like Cicero would write a speech of 5,000 to 10,000 words and then, a few days later, deliver it nearly verbatim from memory. The dominate form of mass communication was through open speeches, and this is why most of the great speeches came from the ancient world. But it was limited; recalling and sharing was easy, but memorizing was still laborious, notwithstanding the fact that ancient scholars' memorization faculties seem like super powers to modern humans.
With the advent of cheap writing and printing, we were able to archive, recall, and disseminate magnitudes more information at a more rapid pace. And while slower to recall, speed of creation and dissemination, and sheer volume more than made up for that limitation. Moreover, mass communication proved far more efficient in written form.
Today computers are killing writing just as writing killed poetry. But it's not really computers, per se. With writing it was the system of creating and using written works (alphabets, printing presses, libraries) that actually realized the efficiency gain. In Alan Kay's view, Lisp is what made the promise of computers realizable, capable of supplanting the dominant form of "thinking" and of opening up new frontiers. Computers are like papyrus; it's learning how to use them that matters, and Lisp was that great leap. It's one thing to laboriously write a program using machine code; it's something else entirely to do this using an elegant, higher-order programming language. Indeed, once you use that higher-order language you can't go back; it changes how you think about programming, including how you structure programs written in machine code. Like with poetry and writing the utilitarian leap in productivity is forgotten because it becomes so effortless and seemingly obvious; instead all you see are the limitations and faults.
So it is just as true as I intended. We live in a world of unsupported opinion. I'm 77 and from a world in which one was supposed to have considerable support for an opinion before voicing it. I realize that this is out of step these days, and especially with web fora.
Qualitatively and quantitatively, working memory capacity strongly reflects limitations of so-called executive attention and, more generally, executive function. See, e.g. https://www.ncbi.nlm.nih.gov/pmc/articles/PMC2852635/ ("Based on the finding that WMC and EF tasks shared substantial common variance, we argue that the current data provide evidence for reciprocal validity for both of those constructs. Reciprocal validity can be defined as a particularly strong form of construct validity, such that two constructs that are strongly empirically related to one another lend support to the theoretical reality of each other.").
AFAIU, the 7±2 quantity is still both valid in its original context and in communicating something substantive about more complex phenomena. Of course, depending on how you "chunk" a "concept" the number quickly becomes meaningless, but that's a game of semantics. The more sophisticated definitions of chunking that one would use to invalidate 7±2 necessarily invoke broader, more speculative models, given that so much about cognition is still unknown. I can't see how one could fairly use such models to invalidate the 7±2 rule, which remains literally and meaningfully true for "chunks" as originally defined. 7±2 communicates something both qualitatively and quantitatively true about something very specific--words, letters, numbers, etc--and about larger phenomena in cognition--constraints on higher-order cognitive processes are reflected by a phenomenon which has a fixed absolute limit, 7±2, in relation to these easily identifiable and measurable inputs--words, letter, etc.
In other words, referring to 7±2 is saying something both concrete and meaningful. Moreover, the conceptual model it invokes is still the dominant framework, if only because the rule circumscribes what it purports to describe, even in common usage. Nobody mistakes a heuristic for determining the length of a phone number (a derivative "rule" which is far more susceptible to the monicker, cliche) with a limit on our innate capacity for conceptualizing Relativity. Examples which invalidate the rule actually muddy the waters by drawing in more incomplete and uncertain premises, exemplifying what the 7±2 rule gets right about how best to communicate something of substance about such a complex and incomplete field of study. Of course, there are limitations to what you can extrapolate from 7±2, but that would be understood.
That's why I think it unfair to disparage application of the "7±2" phenomenon as cliche. Whatever one's specific definition of cliche, I think it's fair to say that labeling such usage "cliche" is to disparage it. I think such disparagement is unfair. It's rare for a simplified anchor to a complex field to retain such validity and power. But ultimately I guess my point rests on what might be an idiosyncratic definition of cliche.
I mean there are a lot of other skills a programmer needs to have than to understand what OOP is, but sometimes I think it would be better if more young students would learn Smalltalk to understand what OOP is about and why the simple existence of classes doesn't make a language an OO language...
I wonder, though, how my OOP thinking would differ if my first exposure was through BETA or Eiffel.
> (These operating systems in both Smalltalk and Lisp were both better (claim) and easier to write (simpler to demonstrate) than the standard ones of today.)
Lisp is a great collection of design patterns for making languages.
> The key was not so much “Lisp” but the kinds of thinking that this kind of representational approach allowed and opened up regarding all kinds of programming language schemes.
> A fun thing about it this is that once you’ve grokked it, you can think right away of better programming languages than Lisp, and you can think right away of better ways to write the meta descriptions than John did.
Once you've grokked it, those programming languages are more advanced dialects of Lisp.
Or else, you didn't really grok it.
Only if you believe that the only good thing one should grok is the data/code parity part.
For me Smalltalk is even better and more concise than Lisp is.
What they were trying to do was demonstrate that they could build the entire system from highest level languages (i.e. the OMeta-based languages they wrote the system in) down to machine code (i.e. what the COLA stuff generated and runtime it provided which would replace the Smalltalk environment) in 20kloc and preserve linkages (i.e. chain-of-meaning) from the top to the bottom. While they may not have technically achieved everything they set out to, I'm sold that they were successful in proving the concept and that they could have gotten there (i.e. the parts they did deliver were impressive) at that order of magnitude in terms of lines of code.
I'm not seeing this from looking at various Rosetta Code examples.
Because we’re not writing these tools in first class language structures, not only do we have to learn 15 different “languages” but we have to wait for each author of each tool to go through all of the stages of programming language design theory. We watch webpack.config.js slowly morph into a poorly designed Haskell, and again and again.
But here’s where you expect me to descend into the classic graybeard “if only these kids would write everything in Lisp and make use of the Great Works of Academic Software” style speech.
No...
Quite the opposite.
I think both camps are wasting their lives. The graybeards are blowing smoke up their own asses about how useful their meta-control structures are, and 97% of those Great Works could be implemented with an isomorphic interface made entirely of functions and literals. The trouble is any person with a masters degree who smells an opportunity to do metaprogramming gets a raging hard on that clouds their judgement until the coding is completed and has a cute name announced on a mailing list. That’s certainly what felt like the glamorous lifestyle to me growing up in the web development world.
But then the Modern coder, using layer upon layer of frameworks and “tools” on a thousand different run times, “getting the job done” and remarking how ergonomic the latest tool is and how much nicer things were than back in the nasty era where you had to write everything from scratch and there were no standards and no consistency... well their codebases, brittle, and calcified by the demands of their customers, are condemned to corrode and become unworkable as declarative control structures rise and fall. Because each piece of the metaprogramming logic is a completely different language, the interfaces between components can’t move as fluidly as a simple function signature, written in the same language your application is written in.
So who cares if it’s Lisp or PHP. Kotlin. Lua. Whatever it is, stop using fancy control structures to prove how smart you are. And stop using fake programming languages that operate as command line parameters or config file grammars, or “Controllers” that magically pick up attributes you sprinkle about.
Then we’ll really find out what Lisp can do.
The problem with the "best tool for the job" is that it leaves us with 2000 tools we need to integrate -- which is an even bigger problem often than what the tools collectively help us solve.
The synthesis will be “it’s OK to use the right tool, but only if your specific needs will play out in a meaningfully better way than the native solution, and keep in mind the cost cliff at each new language or psuedolanguage”.
There are some notable exceptions, of course, but part of this is just inherent to the design of lisp, and I think it's easy to end up with a type safety system that feels more like it's bolted on than part of the language.
[0]https://developer.mozilla.org/en-US/docs/WebAssembly/Underst...
Haskell is also pretty much just a lisp with a whole lot of sugar added on.
To me, a Lisp pretty necessarily needs to treat the structure of its code as mutable data, which seems kind of incompatible?
... both communities care a lot about rose trees?
AFAIK, there are very few truly built in types in Haskell. Most everything can be built out of data constructors and functions. That idea of many things being in language user space is pretty Lisp-y IMO.
Haskell is statically typed, Lisp is not.
Lisp uses s-expressions for writing programs, Haskell does not.
Generally Haskell and Lisp are very different languages: in syntax, semantics and pragmatics (how the language is used).
Lisp images can modify themselves at run-time by replacing global function bindings with new functions, not by mutating code.
Lisp macros must not in fact mutate the incoming source code which they transform.
The same way Lisp is a functional language. It has to do with how both languages are built from only a few basic primitives, and how both are based on Lambda calculus.
I was just thinking this morning about making Arc work in web assembly sometime.
Meanwhile, lisp is metaprogramming. No, it's not useful in many (or even most) applications, but when it is useful, you can bet there's some type of analogous pattern for a lisp-type language. Certainly still useful to learn along side ML. :)
Hell, the best quote to illustrate this is the article itself:
> A fun thing about it this is that once you’ve grokked it, you can think right away of better programming languages than Lisp, and you can think right away of better ways to write the meta descriptions than John did. This is the “POV = 80 IQ points” part.
In this sense, everyone should learn lisp, even if it's not the tool you intend to use.
Of course, it has had it for a very long time (a decade at least?), see camlp4/camlp5.
How easy is camlp4/5 metaprogramming to read and write compared to Lisp macros?
This reminds me of Alan Perlis' admonition to "beware of the Turing tar-pit in which everything is possible but nothing of interest is easy."[1] and of a question John McCarthy asked Peter Norvig after the latter's talk about how Python was a Lisp:
"When he finished Peter took questions and to my surprise called first on the rumpled old guy who had wandered in just before the talk began and eased himself into a chair just across the aisle from me and a few rows up.
This guy had wild white hair and a scraggly white beard and looked hopelessly lost as if he had gotten separated from the tour group and wandered in mostly to rest his feet and just a little to see what we were all up to. My first thought was that he would be terribly disappointed by our bizarre topic and my second thought was that he would be about the right age, Stanford is just down the road, I think he is still at Stanford -- could it be?
"Yes, John?" Peter said.
I won't pretend to remember Lisp inventor John McCarthy's exact words which is odd because there were only about ten but he simply asked if Python could gracefully manipulate Python code as data.
"No, John, it can't," said Peter and nothing more, graciously assenting to the professor's critique, and McCarthy said no more though Peter waited a moment to see if he would and in the silence a thousand words were said."[2]
[1] - https://en.wikipedia.org/wiki/Turing_tarpit
[2] - http://smuglispweeny.blogspot.com/2008/02/ooh-ooh-my-turn-wh...
Having implemented a non-trivial 'macro' in camlp4 and likewise in Scheme, I can confidently say: It's about the same level of difficult/pain. Which is to say that it's much too painful/annoying.
Though, I should say that Racket by all accounts from the literature has improved things massively, even going to so far as to implement "macro-based" type systems (search for "turnstile racket").
EDIT: Just a side point, but:
A witty saying proves nothing
- Voltaire
(No idea if Voltaire ever said that, but that's kind of the point.)You probably meant to contrast ML's static typing (not strong typing) with Lisps's dynamic typing. See "What To Know Before Debating Type Systems":
https://cdsmith.wordpress.com/2011/01/09/an-old-article-i-wr...
> The functional and imperative languages are weakly typed, i.e. the result of computing the value of an expression e of type T is one of the following: [...]
http://wiki.portal.chalmers.se/agda/pmwiki.php?n=ReferenceMa...
As a matter ordinary meaning, I also feel it makes sense to talk about more strongly typed languages, because they can express 'stronger' guarantees about behavior. Eg. a proof that the code cannot deadlock.
'Compile time', like everything in the artificial science of computing, is a made up thing. We might reconsider if it's a useful idea to keep. Why must there be a specific point in time, when we verify interconnections within a little bundle of code, but later when the bundle of code integrates with a larger system we are completely fine with very different, loosely coupled connections?
The internet is dynamically typed. I just don't see static verification scaling to that level. But verification is still great - so maybe we need a way to verify connections between components as and when they bind. If we build a good model for this late bound 'negotiated safe' binding, the static model might become unnecessary (because you can do the verification whenever you choose, or at multiple times and different scales, as the 'bundle of code' integrates with larger and larger systems.)
Which mathematics?
(Lisp has its own math, for instance.)
> The internet is dynamically typed.
That's an incoherent claim.
Dynamic typing is where types belong to values. Static typing is where they apply to terms.
Really to call the former "typing" is misnomer. It's an abuse of the word "type" to mean "memory mode". It has not much relationship to typing in the logic/math/philosophy of logic sense in which types are sets and type relationships are those between sets.
It is the Curry-Howard isomorphism (https://en.wikipedia.org/wiki/Curry%E2%80%93Howard_correspon...) which defines the correspondance between types in computer science and mathematics.
This is "static" only because the "dynamic" alternative isnt really an alternative at all, and really just a different kind of thing all together.
The claim is logically incoherent: typing cannot be a property of the internet, in the same way tuesday cannot be pink nor can addition sound terrifying.
The claim is a category error: the internet cannot posses such a property.
In addition, to say, of something, that it is " is a made up thing " is a degree of hubris that is presumably reasonably met by being perplexed at overconfidence of the claim posed with such little correct information.
Is being perplexed more uncivil than hubris? Are we really policing people's language to this minute degree?
Actually you broke this guideline as well: "Please respond to the strongest plausible interpretation of what someone says, not a weaker one that's easier to criticize." It's possible that shalabhc intended to make a mathematical claim, but a stronger plausible interpretation is easy to come up with (e.g. "it's hard to enforce static guarantees across the software that interoperates on the internet").
The reason we ask people to follow this guideline is not so much that it's nicer, but that nitpicking things each other probably didn't say is tedious and detracts from good conversation, which is what we're really hoping for.
I agree there are correctness properties we can prove without running a 'program', but how do we map the notion of a 'program' into the real world. Is a single function a program? A single module which includes multiple functions? A single executable? A single system that includes multiple processes communicating over a network? I'm arguing each of these is a 'program' and a Turing machine in the theoretical sense. Each of these programs is hooked up to other 'programs' outside of it. 'Compilation' requires the input to be static, but if we think about how things are in flux (you can change a function, switch out a shared library or upgrade and restart a running process, etc.) when do you verify that a 'program' is 'correct'?
This is what I mean by 'compile time is made-up' - it falls out of the current frame of thinking of one OS process = one program = static set of source files. It is possible to design systems that have no notion of 'compile time'. You could still have verification, but it could be incremental and spread out all through the lifetime of the running system. So the system would have no 'compile phase' - it would be running live and as you update parts of it, the updated parts would integrate with the rest of the system and do verification like things.
But what's the difference to say, having just a single dynamically typed language with library functions "type-check-my-code", which returns an encapsulated value, and "run-my-typechecked-code" which takes the encapsulated value as input? The whole process happens at "runtime" here.
The artificial static/runtime boundary is introduced by the way Unix implements files and processes. All statically typed languages are effectively supersets of the language of Unix. In Unix we can treat each executable binary as a library function (except that we have a massive overhead for calling it). The designers of Unix were well aware of this which is why from the POV of bash, there's no distinction between invoking a binary and invoking another script written in bash.
How much of the Unix binary and process bloat is really necessary though to say, concatenate N files? Even someone writing a C program will avoid calling cat and instead implement it themselves, or call a library that does it. Perhaps we need to reconsider where the boundary between type checking and running code should be.
Example: You could think of an interpreter for a statically-typed language running various processes as the same program, but we still say the language is statically typed.
Is it statically typed for cross process messages though?
A solution that is often overlooked is to simply shrink the set of legal programs. Simply typed lambda calculus for instance has a perfectly decidable halting problem (which is, programs written in it always halt).
There are 2 ways to handle undecidable properties with static analysis: either reject programs for which you can't prove the property holds (you will reject correct programs), or accept programs for which you can't prove it doesn't (you will accept incorrect programs, and may use runtime checks to compensate).
Rejecting correct programs is a problem only to the extent one would like to write such a program in the first place. Take this expression for instance:
if 2 + 2 = 4
then "Yay!"
else 0
It is a perfectly fine expression, of type `string`. Most type systems will reject it however because the two branches of the conditional don't have the same type. Thing is, we don't care about this program in practice, since the constant test expression screams code smell to begin with.It doesn't matter which property you are looking at. If it's undecideable, you can sidestep the issue by forbidding programs for which the checker is not sure.
That doesn't mean you have to lose Turing Completeness. You just have to chose properties that don't imply that the program halts. Static typing for instance doesn't mean the language isn't Turing Complete. Simply typed lambda calculus is the exception here.
I sometimes workaround this by proving for "outputs the correct result, or loops forever". Here, you don't have to prove for halting, since your program is not expected to halt in some cases anyway. But then in some particular cases, you can prove that your algorithm is correct. You do not have to lose Turing-completeness to do this. But it gets trickier when you want to built a type system that can give all the bugs in your program, in general. You can definitely build something that finds all the bugs "for all intents and purposes" but not something that can be mathematically proven that will give you all of them. To be concrete, you can build a program, given arbitrary Python code, gives most of/all the bugs in this program. But you cannot build a program, given arbitrary Python code, that is proven that it will correctly know whether this program will output correct result for all possible inputs. You can do this for some restricted Python programs (as you've been arguing for last two comments) but in general you cannot do this, which brings us to this fundamental division.
In practice, I've been able to accomplish things with static typing that I hadn't the brainpower to do with dynamic typing, because even with a REPL, runtime errors came too late, and didn't meaningfully informed me of my mistakes.
As for why we switch to dynamic typing as we scale up, some of it probably has to do with trust. Computers communicating over the internet cannot trust each other, if they can even trust they are talking to one another to begin with. They have no choice but to perform lots of runtime checks to make sure they don't get pwned by the slightest remote mishap. Once you get to this point, static verifications become much less useful, and can often be skipped altogether.
I'll note that we switch to 'dynamic' much before the question of trust arises. Whenever you do IPC between two OS processes you fully trust, communicate through a database or file, wire up a multi-process system with config files, etc. Most internal code at an organization is trusted, yet the communication between OS processes within an company uses dynamic messaging semantics.
I'll also point out that if you use a compiled shared library, or any system call you are forced to use the type system of C, which isn't particularly advanced.
It could be that we haven't developed dynamic messaging protocols that can bind safely and correctly. I'm arguing that if we have these, they might apply on a smaller scale too (objects within a 'process' or connections between shared libraries, etc.)
> Static typing enables faster feedback loops.
All of that only works on the small scale - where the type knowledge has to be fully shared within the entire program. As soon as you're writing code to call a service, static typing benefits are moot. The current solution is to create 'stubs', which doesn't scale well either. I am not arguing for 'dynamic typing everywhere', but rather about a different POV where we start looking at 'interconnections' at all levels the same way, and think about making the introspection and safe late binding in a standard, scalable way. Minimizing pre-shared knowledge would be one way to improve how this scales, for instance.
It is not.
We trust our colleagues not to be malicious, but we generally don't trust them not to make mistakes. Hence defensive programming.
The point I'm trying to make is that 'static-typing style verification' which happens across different parts of a 'single program' doesn't extend to multiple programs or real systems. Maybe we should look at some kind of late bound dynamically bound verification - i.e. a protocol that is executed whenever one 'program' reaches out and connects to another, to determine if the connection is safe and correct. Do you think this has value?
Hah. Not everyone can be that cool.
Lisp is arguably the most influential programming language, that doesn't mean it is objectively 'the best'.
The words he says are nice, and his depth of knowledge is profound, but he hasn't actually provided any evidence making the case that lisp is better and/or in what scenarios it is better.
That this sort of rhetoric is the way almost all programmers and language designers try to find truth and communicate ideas is probably why we ended up all programming in javascript. We have no rigor, we are like Freud era psychology with flowering and impressive sounding prose but no real data. And while psychology still has a long way to go, we have even further.
I'm very much a pragmatic kind of person, and my unpopular opinion about code is that Lisp is the greatest single language ever designed, if your goal in designing a language is to produce articles proclaiming it as such.
And I am sooo sympathetic to the promises Lispers make. I want to believe. But ultimately, show me the code. Why are the stellar Lisp success stories so few and far between that one of the big ones is Paul Graham's from the 90s?
If it's so powerful that almost no one can use it, and it rules in almost no problem domains, ... how is it so powerful again?
So, yeah, you're unlikely to see that many "killer apps" made with it, as the killer apps of today usually require a lot of programming resources that the Lisp community just doesn't have. What apps Lisp programmers do come up with are mostly not going to be so uniquely Lispish that they couldn't be replicated in another language (see Reddit's rewrite from Lisp to Python[1]).
On the other hand, there's Emacs. That's one killer app whose power arguably hasn't been beaten or replicated elsewhere. But that very much depends on your taste -- and if you agree with that you're probably already a Lisp convert.
Then there's the whole "worse is better" thing. All sorts of inferior technologies have triumphed in the marketplace -- for reasons that usually have little to do with their technical merits. Lisp has itself been a victim of that. It's a vastly superior language to so many more popular ones out there (and many other language designers agree, if the regularity with which Lisp features are pilfered in to other languages is anything to go on), and yet so many programmers are really ignorant of it or have only been exposed to little toy Lisps in their college years, mistakenly thinking that modern Lisps are no better, don't realize how much their own favorite languages owe to Lisp, nor how much more that their language doesn't have that Lisp has to offer.
[1] - https://web.archive.org/web/20051208055316/http://reddit.com...
Couldn't you substitute calculus, real analysis, or abstract algebra for Lisp in this statement and make the same claim?
The idea---not just to think about programs per se, but to think about abstract properties and operations of certain category of programs and to be able to actually write it down---gave the exponential power to you. Lisp was the first such tool as a programming language that allowed you to break the ceiling of first-order thinking.
Nowadays there are other languages that provide meta-level tools. And once you get the idea, the language doesn't really matter; you can apply it to any language, albeit you may have to build scaffolds around it to compensate lack of language support (Cf. Greenspun's tenth rule). So, if you already got the idea through other languages, it's good for you. You'll see Lisp as an arcane prototype of modern languages.
But note: since Lisp can write meta-level description in the same language as the base language, you can top up this ladder infinitely, meaning you can think of program-generating-program, then program-generating-program-generating-program, and so on, using a single set of tools. It is indeed powerful but somewhat crude, so modern advanced languages intentionally restrict that aspect and just try to cover the sweet spot. Even if you're familiar with metaprogramming, it may still be worth to play with Lisp to see what (meta)*programming is like, to know its pros and cons, and to get the idea that might come handy someday when single-level metaprogramming isn't enough.
If you remove their name from the things they have said or written, at least many of them seem fairly average sensible statements, like something you would read here from a random member.
I'd be interested to see a "blind test" for these things. I bet the bias would show.
As in, display something from Alan Kay but post it on a random internet forum as JoeSchmo11, and you will have lots of people disagreeing with it and tearing it apart without hesitation.
But oh look if Steve Jobs or Alan Kay or Elon Musk's name is attached to it suddenly becomes a unique gem of knowledge to be cherished by the humanity for the rest of time and surely the fault lies with anyone not agreeing with it right? Because after all they could not possibly be more right than those authority figures?
I'm not making a judgement about this, neither am I surprised by it. Just saying that you should ask yourself exactly how much of your own brain power and judgement do you use to judge the quality of an idea and how much of your conclusion is conveniently borrowed from your image of the author?
It reminds me of when Elon Musk talked about the probability of our universe being a simulation.
I'm not saying he is right or wrong, just using that as an example. But if JoeSchmo11 said exactly the same thing people would just dismiss it.
But when Elon Musk says it all the newspapers go mad and write stories about it.
Again I'm not saying it shouldn't be like this, reputation matters, I get it.
But my point is that the fact that this happens to the degree that it does is in some ways a sad admission that we are incapable of evaluating thoughts and ideas independently of their originators and on their own merits. Instead we borrow from the past credibility and the authority of the people they are attached to.
Also, in this particular case, my point wasn't really about the truth value of what he claims (which I'm not fit to judge), but about the clarity of how he claims it.
If I don't know anything about X, and the Alan Kay of X comes and tells me something about X, it's pretty likely to be true. If equally-ignorant-as-me-person tells me something about X, it's not as likely to be true. That's the meaning of knowledge and ignorance.
None of this precludes thinking for yourself about anything. It's just the thing you do in addition to thinking for yourself about it, if you want to have true beliefs.
Lisp provided concepts captured enough structure that it cut through a lot of combinatorial complexity in the space of possibilities, making reasoning about the construction of computer programs and algorithms a lot less random.
A complaint might be he left out what aspects of Lisp provided that combinatorial boost but I'm sure many HN users could furnish those reasons. But one easy observation is the sheer number of languages and language concepts that are rooted in it. Newtonian physics is not the best physics but it fundamentally changed the way people reasoned about the world. Lisp is not the best language but it fundamentally changed how people reasoned about computer programs.
One objection one might make is that the real Maxwell/Heaviside Equations can instead be found in the Lambda Calculus/ISWIM. Despite the name, Lisp did not originally derive from the lambda calculus but just about all typed functional languages and future versions of lisp are of the LC. But a quick answer to that is influential languages like SASL and Meta Language were prototyped in and strongly influenced by Lisp (cognitive boost).
In programming (assuming a "computer language" as an analog to "axiomatic system"), there are abstractions that cannot be implemented. You can only implement them by going up a level.
In LISP, you have this "going up a level" ability with macros. The only reason this works for LISP is that the language is written in terms of data LISP can manipulate, meaning you can manipulate code at compile time. Forth has an ability to "go up a level" as well, with a defined way to extend the compiler.
He said it right there: “unlimited programming in an eyeful” (the bottom half of page 13 in the Lisp 1.5 manual)
It sounds like all the knowledge should be delivered right into someone’s mind in a couple of minutes. It is right there, go and learn it for a week at least.
When I try to directly convince random internet people that e.g. Lua has better async[1] than js due to language primitive that is more abstract and cannot be reimplemented in lower-order primitives, all I hear is usually butwhys and dontneeds, real data on both sides (at best; at worst they claim this code cannot be asynchronous). Instead, folks stick to cumbersome syntactic methods fitting their evolved familiarity. They don’t need anything beyond js, that’s why there is js. And this is one small step torwards less cognitive load and yet we have a friction. Good luck explaining macros, call/cc, evaluation models, etc to them to the point they actually get it en masse.
That said and to be honest, I never mastered lisp skills because it is not actually easy (and I’m not very smart too). You enter the completely new and broad area, where you almost have no clue what you can do. The same would feel a junior entering, say, java after dos basic. But more confusing, because junior has lots of time and is just learning and you’re a professional that “already knows it all” (not really) and has little/no time.
Every effort required some first principles rationalizing to justify the R&D.
Alan, and others from Xerox PARC, have a very "meta" outlook on the entire field.
Bastard-OO is widely successful in everyday programming because classes enforce modules + mandatory interface definitions. They restrain spaghetti code inside smaller entities that are easily managed. Software architect can define these entities and give them to code monkeys to fill. Static compilation can do rudimentary validation. It's not small feat and enables some scaling, but it's dirty and not really OO.
To quote Alan Kay:- "I don't think I invented 'Object-oriented' but more or less 'noticed' what was really powerful about just making everything from complete computers communicating with non-command messages. This was all chronicled in the HOPL II chapter I wrote 'The Early History of Smalltalk'."
Nevertheless, consider a couple of modern widely-used languages. C++ in terms of object-orientation, is based on Simula, and Java's object model is entirely based on Simula.
I argue that it was Nygaard and Dahl who first introduced those language's key concepts (objects, classes, sub-classing and virtual functions) in a coherent and programmable form. This is why Nygaard's Turing Award page states "Kristen Nygaard is internationally acknowledged as the co-inventor with Ole-Johan Dahl of object-oriented programming and the programming language SIMULA", and why I stated in my parent response that you named the term others invented, notwithstanding that today's common use isn't necessarily consistent with your coining of the term.
I suspect that there are a lot of people still around who encountered other OOP models before C++ and it's derivatives became dominant for whom that's not really true.
> "The Scandinavian view is that an OO system is one whose creators realize that programming is modeling. The mystical view is that an OO system is one that is built out of objects that communicate by sending messages to each other, and computation is the messages flying from object to object. The software engineering view is that an OO system is one that supports data abstraction, polymorphism by late-binding of function calls, and inheritance."
Most today (sadly) know only variations of the Software Engineering viewpoint.
Alan Kay's definition of OOP:
>OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things. It can be done in Smalltalk and in LISP. There are possibly other systems in which this is possible, but I'm not aware of them.
http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay...
kay2@apple.com.Internet#
A year or two later, there would be practically no other email systems than “Internet”.