Hacker News new | past | comments | ask | show | jobs | submit login
Alan Kay on Lisp (quora.com)
427 points by shakes on Oct 27, 2017 | hide | past | favorite | 193 comments



Actually, I am missing the simplicity of Lisp and Smalltalk in todays languages. From what I remember, they both have a fairly simple but universal syntax, which can be written for Lisp as

  ([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.


smalltalk cutest feature is the named parameter syntax trick (I don't like tricks but let's make an exception).

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.


> metamodel is actually human reasoning friendly

There's so much power in being able to think about a language (or your application) without having to always dig into the code.


"smalltalk cutest feature is the named parameter syntax trick..."

  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?


So the example would be:

   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.


Found the paper on generalized method names in Grace:

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...


Sorry to "well actually" you, but

    1 to: 10 do:[ :i |  <do something with i> ].
would be a single message `to:do:`, not a chain of messages.


well actually-actually :-),

   (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.


> well actually-actually :-),

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 ;)


Yeah. My reply was more meant to show off more Smalltalk to pmoriarty and others that to one-up everyone :-)


Yes, I left that detail out for pedagogical purposes.

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.

[1] http://objective.st/

[2] https://en.wikipedia.org/wiki/Higher_order_message


It's keywords as sentences. It basically allows a function signature to be spelled out as a sentence with infix operators. These days you can see the lineage of smalltalk in keyword arguments. For example, python vs. smalltalk:

    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.


In Smalltalk, the 'keywords' are actually part of the method name; when you say '1 to: 10 do: [ ... ]', you're not calling to: on 1 (which is an entirely different method) with a keyword argument, you're calling to:do:.

> 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).


Yea I was eliding some points to try and explain it better.


You can put the parameter names in method calls, which makes for much more readable code. Like a makeRange method may take parameters `from` and `to`, when calling this method you could do `makeRange(from: 0, to:10)`.

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.


> makeRange(from: 0, to:10) [..] Swift’s guidelines on naming methods.

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).


AppleScript may be the only read-only programming language in existence.


Depending on your definition of programming language, that crown might actually go to HAGGIS. It's an "educational" programming language which is not executable (or intended to be). It's possibly the worst idea in the history of computing, and that it is taught to anyone is (IMO) a public offense.

https://en.wikipedia.org/wiki/HAGGIS


"ah, scary, close tab!"


I get that vibe from Inform 7: the famous Dijkstra’s algorithm reads like English, but I'd have no idea how to write such a program.


Inform7 is mostly for text adventures. I'm sure it's not bad if you give it a go as many people with little to no coding experience write expansive text games using it.


I am still waiting for the combination of Perl and Applescript.

You have to write it in Applescript, but you get to read it only in transliterated Perl.


"You can put the parameter names in method calls, which makes for much more readable code. Like a makeRange method may take parameters `from` and `to` ... That’s a fairly trivial example but it demonstrates how readability is improved."

Is that something like keyword arguments in Lisp?


Except Smalltalk keywords can't be reordered or omitted. It's more like you put the arguments in the middle of the method name, like a kind of mixfix notation.

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.


> smalltalk cutest feature is the named parameter syntax trick (I don't like tricks but let's make an exception).

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.


> by making all the parameter names mandatory

For example?


In smalltalk the parameter names aren't mandatory in the sense of many other languages.

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:"


I asked hota_mazi to provide an example which demonstrated what they were talking about, to explain why they said "Smalltalk got it wrong by making all the parameter names mandatory".

Are you hota_mazi ?


In languages where parameter names are optional, you can say

    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.


I don't think any of us know what:

    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.


I tried to pick up an example as generic as possible so you could understand my general point instead of focusing on this specific example, and you choose to focus on this specific example. <sigh>.

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?


So you mean that in this case we might assume the conventional order x coordinate first and y coordinate second would be understood.

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).


> So you mean that in this case we might assume the conventional order x coordinate first and y coordinate second would be understood

... 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.


> … you focus on the specific example…

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.


Isn't Objective C exactly that, a C family language with a Smalltalk inspired syntax?


ObjC is a completely backwards-compatible superset of C that bolts a Smalltalk-like syntax on top. The first implementation was a preprocessor that emitted C, as far as I remember, until first-class compiler support arrived (GCC and Clang have native support).


It's out of favor now, but Objective-C? IIRC it was inspired by Smalltalk's message syntax.


That was the feature I liked the most from my Objective-C days in the early versions of Mac OS X, it felt a bit like SmallTalk even if it was just named parameters and messages.


It's not named parameters though, it's compound message/method names. The arguments are interspersed within the "bits" of the method unlike e.g. Python which has named parameters separate from the method name.


Ruby is nice for this. It has message-passing OO and clean, minimal syntax.


Lisp, Scheme, and Racket and related languages have many distinct syntax forms, just like other languages. The syntax forms appear visually similar due to the use of parentheses, but the forms themselves are distinct.

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.

See: https://docs.racket-lang.org/reference/syntax.html


This is why I love John Shutt's Kernel[0]. All of these "distinct forms" are rolled into one form of "(combiner combiniend)", where combiner is runtime evaluated and can either be a regular applicative (like a lisp procedure call), or an operative (reminiscent of older lisps fexpr). Compound operatives can be created using the primitive operative $vau. Applicatives are created with the primitive wrap. (which $lambda uses in the standard library).

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).

[0]:http://web.cs.wpi.edu/%7Ejshutt/kernel.html


Thank you for sharing this. I hadn't heard of Kernel, and it seems to be highly relevant to a line of inquiry that I've been pondering for some time (you could say light research) in the area of language design. There seem to be a lot of interesting topics in the related dissertation [1]. Thanks again!

[1] https://web.wpi.edu/Pubs/ETD/Available/etd-090110-124904/unr...


I think your examples underscore the GP's point.

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.


The notation uses S-expressions, that’s true, but the point that I was trying to make is that you can’t understand anything about the meaning of those S-expressions without parsing them in a context-aware way.

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.


Strictly, this isn't true. In f(a, b), if f is while, it is not a procedure call. Or if it is a declaration.

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.


Right. There's still syntax, but instead of that syntax being defined directly in terms of characters and tokens, it's defined in terms of s-expressions. Thus s-expressions serve as an intermediate syntactic level between tokens and forms. (Forms are what other languages call expressions and/or statements.)


Actually, almost of those are just function calls. Some of them happen during the compile phase, some during runtime. Things that happen at compile-time, say a let function call[0] e.g.

    (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.


Most of the examples I gave above are core syntax forms. They are defined in the language grammar and are not function calls or macros. See the Racket language reference [0]:

> 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/


Good explanation. Many people get that wrong and think the syntax for s-expressions is the syntax for Lisp.


The problem with this argument is that LISP was popular when there was no need to build extremely complex systems with a programming language.

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.


that this has an unbalanced paren has me laughing a lot more than it probably should


Posting Alan's reply as a comment below for posterity, since Quora appears to forbid archive.org from making a copy.

---

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!”


And these operating systems in both Smalltalk and Lisp were both better and easier to write than the standard ones of today.

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.


For example, https://github.com/tonyg/pi-nothing/blob/master/kernel.nothi... does something similar to that blinker. It might not be quite what you're after, because it's only superficially a lisp; more an S-expression veneer over something C-like. Toolchain is the rest of the repo. Currently written in Racket. (BTW this is feasibility-study-level stuff, not production stuff. It's messy as hell because I haven't done even the most rudimentary tidying-up of it for public view. Caveat lector.)

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.


The way things stand now, the only required piece of application software that a new personal computer needs in order to be viable is a web browser. But once you have that, and the system of your machine is sufficiently compelling, it would work for a great many people.

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.


Well, wouldn't that just be a JavaScript machine implemented in Smalltalk?


All I meant by bringing up the web browser is that for a personal computer to be usable in reality by a lot of people they'll need a web browser. But just that user application alone gets them really far: email, office productivity, etc. It's a good first application to have in any system.

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.


Agreed. I think the best way would be to use Linux as the kernel, maybe with Wayland for display, and Chromium/Firefox in a sandbox, and then the custom OS system running alongside this. Emacs with the Elisp window manager is an example.


I don't think we're on the same page here. You don't need Linux for the kernel. Insofar as there's a kernel, it's the programming environment itself (like Smalltalk) or a set of primitive programs written in that environment (like Lisp).

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.


Oh. For me it's just pointless drudgery to remake everything like that. I want a new fully coherent and portable system using an existing kernel and graphics layer for bootstrapping. I mean, Emacs or Squeak are already almost there as far as I'm concerned.


Could you elaborate? It's a bit cryptic to me, but it sounds exciting!


It's possible to develop a personal computer whose hardware has been designed to run a programming environment as its operating system. In fact, these existed before. Lisp machines did it, and Smalltalk was developed precisely as this kind of system (on the Alto).

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.


> is Alan Kay for real? Where's the Lisp equivalent to this C code?

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

http://www.vpri.org/pdf/tr2008004_steps08.pdf

http://www.vpri.org/pdf/tr2007008_steps.pdf


No, I hadn't heard of STEPS. Thanks for the links!



That's great but it isn't the same thing as what I linked to, which is a small program loaded by the Raspberry Pi's boot loader.

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:

https://github.com/freebsd/freebsd/tree/master/sys/libkern


Thanks for posting this. Much easier to read here.


it is the man himself :), and ofcourse, this p.o.v is worth 80 IQ points. this insight is

'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


It's probably more literally true than he intended. And 7 isn't really a cliche; it's not an opinion but a thoroughly proven, universal limitation of human working memory.

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.


The 7±2 was thoroughly proven for sequences of numbers, letters, words, etc. by George Miller and followers. Much has been done since, and this shows that for many things we can handle fewer chunks. That "7±2" is still used for the larger idea of "cognitive load" makes it a cliche and a metaphor.

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.


To my mind a cliche refers to an archaic metaphor. Archaic because it out-lives the discarded conceptual framework it describes, and therefore no longer communicates anything of substance. Worse, such cliches often perpetuates err in conceptualizing an issue.

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.


A similar recent answer on Smalltalk: https://news.ycombinator.com/item?id=15518746.


Well, there are surprisingly few CS graduates who actually understand Object Oriented Programming as Alan Kay designed it for Smalltalk, which in turn lowers the probabilty that any of them will build a qualitatively better language.

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...


OOP was taught to me using Smalltalk (VisualWorks actually). It was a PL class, so it didn’t go deep, including also Lisp, Prolog, and SML.

I wonder, though, how my OOP thinking would differ if my first exposure was through BETA or Eiffel.


It could be different, but might not be. Exposure to different languages can later broaden your horizon. I started with programming with a programmable calculator, then BASIC, Assembler, Pascal, Modula 2, ... which then typically leads with Wirth moving into OO with Clascal, Oberon, Object Pascal, ...


I like how Alan Kay still ends up using nested parentheses when discussing Lisp:

> (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.)


I disagree with the "single ... language" right off the bat before any deeper considerations.

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.


>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.


It seems like it wouldn't be hard to make a Smalltalk-like sub-language inside a Lisp dialect like Common Lisp or Racket. It could be programmed using S-exps, or custom read syntax to make it look like actual Smalltalk.


Might be interesting as an academic exercise but doesn't seem like a terribly useful thing to actually do. I like both languages but using Smalltalk syntax in Lisp isn't something I'd want. Besides, Lisp has (IMO) nice optional keyword parameter syntax which fits better in the Lisp world than the Smalltalk message/block syntax ever would.


You and I might not want a Smalltalk syntax in Lisp, but someone who favors Smalltalk (like user coldtea to whom I was responding, who finds that Smalltalk is "better and more concise") might want that.


Fair enough. I was reacting more to what I interpreted as you advocating the approach as opposed to just trying to offer a solution based on coldtea's comment. (I likely had a visceral reaction because I've actually implemented a toy Lisp in Smalltalk and it wasn't something I'd want to do much more than play with)


Have you looked at Ian Piumarta's work with COLAs? Curiously it's a mix of a Smalltalkish higher level object language with a lower-level lisp like language. I never quite understood how they worked together, but maybe you can understand the papers over at vpri.org better than I.


I'm somewhat familiar with Ian's COLA work but am much more familiar with the Smalltalk related portions of the project since I'm currently working with some of those pieces of it. As I understand it, the original intention was to prototype the system in a Smalltalk image and eventually re-target the work to his COLA architecture via OMeta (which the higher-level stuff was written in so retargeting would be done via OMeta parsers and not require many (any?) higher level changes) I think they got some of the lower level things running that way (i.e. I think I recall seeing a demo of at least part of the Nile/Gezira work running as 'native' code but could be remembering it wrong) but due to time/money constraints I don't think they ever got completely there with it as their final demonstration artifact (FRANK) was Smalltalk image-based (plus a couple of plugins for performance). Take this explanation with a grain of salt as I admit to being a bit fuzzy as to how far they got with Ian's lower-level work...

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.


> For me Smalltalk is even better and more concise than Lisp is.

I'm not seeing this from looking at various Rosetta Code examples.


I think lack of understanding the data/code parity is a huge part of why so many development ecosystems are broken right now. The lack of metaprogramming in other languages has spawned this proliferation of declarative programming that results from the use of build systems (config files are declarative) and filesystem tools (git, ember-cli) and psuedolanguages (SQL, jquery selectors).

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.


That's an interesting take on the matter -- and I also agree that while this is a real problem, Lisp-everywhere or X-everywhere is also not necessarily the solution.

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.


Yes, that’s the Hegelian antithesis to the “Language Wars” of the previous decade.

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”.


Operating system written in lisp: https://github.com/timonoko/Nokolisp_On_Nokia_phone


There seem to be two big camps that preach a "learn this weird new programming paradigm because it'll help you see things from a new perspective and will make you a better engineer" message: the lispy languages, and the ML-like languages. Both of these languages categories give you functional programming as well as metaprogramming, which is great. Having tried both, I've found that the ML-derived languages tend to have a stronger type system that's more rigorously enforced at compile time. I personally find this much more valuable in practice than some of the dynamic magic that you can do with lisp.

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.


There are also plenty of Lisp programmers that think that it's a real tool to solve real problems, not just an educational oddity. There are a bunch of Lisps out there, and plenty of them are by no means toys. My laptop's initial RAM disk and init system are Scheme programs.


And it's not a Lisp per se, but WebAssembly's text format uses s-expressions[0]. It's likely that the "oddity" of Lisps will seem less odd as adoption increases if they stick with that.

[0]https://developer.mozilla.org/en-US/docs/WebAssembly/Underst...


I had no idea they did that! Thanks!

I was just thinking this morning about making Arc work in web assembly sometime.


gcc’s IR, “GIMPLE”, is also commonly printed in s-expression form.


As is LLVM's. I think JVM's might be too, but not 100% sure.

Haskell is also pretty much just a lisp with a whole lot of sugar added on.


How is Haskell a Lisp?

To me, a Lisp pretty necessarily needs to treat the structure of its code as mutable data, which seems kind of incompatible?


The evaluation model of both is quite similar though (reduction), and you can see it in the similarities between Core Haskell and Core Scheme.

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.


The evaluation model is different. Lisp is using strict evaluation and Haskell is using lazy evaluation.

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).


No, it actually doesn't; that is a common misconception.

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.


> How is Haskell a Lisp?

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.


An ML is also not a Lisp, though.


> How is Haskell a Lisp?

... both communities care a lot about rose trees?


If you squint right, both monads and lisp S-expressions are rose trees.


Not to mention that one can get static typing in Lisps, if one wanted. See Typed Racket, for instance:

https://download.racket-lang.org/docs/5.0/pdf/ts-guide.pdf


I just listened to an interview with Philippe Kahn and he mentioned that the language he uses today is Scheme.


what are you using? I want to try it.


Not GP but could be Guile https://www.gnu.org/software/guile/



"Having tried both, I've found that the ML-derived languages tend to have a stronger type system that's more rigorously enforced at compile time. I personally find this much more valuable in practice than some of the dynamic magic that you can do with lisp."

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...


Sure, I meant static types + Hindley-Milner type inference, leading to a "type system that I like and feel comfortable with" (per your link), i.e. a stronger type system.


For another (type-theory) perspective - a 'dynamic language' can be automatically considered weakly typed if it can return an runtime exception due a failure to perform a runtime type coercion, or because the typesystem is not expressive enough to include a proof of totality. That makes the whole dynamic/static versus strong/weak distinction kind of meaningless.

> 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.


> more rigorously enforced at compile time

'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.)


"Compile time" is artificial, but the difference between static and dynamic is natural (in some sense, I realize these words are quite slippery). It emerges from the underlying mathematics. There's a real difference between correctness properties that I can prove without running my program and correctness properties that will introduce failures at runtime.


You're using a dynamic language (bash) to invoke your statically-typed-language-compiler and then to also invoke the binary it produces.

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.


runtime here means the runtime of target program, it has nothing to do with unix. If your program fails while executing due to a type error, it's not static type-checking.


I think sparkie has a very valid point. It has to do with Unix because a 'program' in Unix has specific meaning (and Windows is no different in this regard, btw). A Unix program doesn't map exactly to what a program means in the theoretical sense. E.g. two Unix processes talking over a socket are not thought of as single Unix program, even though they can be considered a single theoretical program. So there is a tendency to think in 'processes' not 'systems'. I elaborated a little more about this elsewhere in this thread (https://news.ycombinator.com/item?id=15581591).


But it has nothing to with typing, as typing means tying to a location in program, you can have processes communicating part of the same program, but the type-error will refer to a part of program and when the type-error is detected i.e. during execution of that part of program or some external part of program is what we are concerned here.

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.


> 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?


There certainly is a fundamental difference between what programmers refer to as compile-time and runtime. Naming is probably problematic; but mathematically you cannot prove all the properties of a program just by looking at its syntax, in general. Because this implies a solution to the halting problem. In other words, there is no program, given your program, that can verify it; whereas, there exists a program, given an input and your program, that can verify that your program outputs the correct thing (as long as the language can be emulated by a Turing machine). When it comes to programming languages, the distinction can be trickier, since you can technically get a python program and statically analyze it. So, "compile" time and "run"time are not necessarily perfect words but they belong to the programming terminology. The more crucial thing is, there is a mathematical difference between those two.


> mathematically you cannot prove all the properties of a program just by looking at its syntax, in general.

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.


Yeah, but now you're arguing something else. Simply typed lambda calculus is not Turing-complete. With a Turing-complete language, you cannot decide the halting problem of an arbitrary sentence of that language. With a non-Turing-complete one, you potentially can. For example, Charity is one such non-Turing-complete language, for which, hypothetically, one can find an algorithm to decide halting of an arbitrary program. But most languages today are Turing-complete, because TC is a very easy property not to have, and it is very hard to construct non-TC but useful languages (eg, C++ metaprogramming is accidentally Turing complete, so is HTML+CSS). So, while what you explained is correct, it does not have direct implications on languages like Python, C++ etc. We still have different sort of properties while we're statically analyzing a TC language, and executing it with an input. This was what I was trying to explain.


Being Turing complete doesn't mean you cannot prove anything without solving the halting problem. Programs have many potentially undecidable properties, and halting is only one of them. It's also one of the strongest.

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.


Where did I claim "you cannot prove anything without solving the halting problem" or "static typing means non Turing complete" exactly? I said nothing of this sort... IN TC languages you can prove some properties, but in general you cannot prove your program is correct. When I say in general, I mean exactly that. You keep bringing some specific cases where you can prove correctness, but that's beyond my point.

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.


> It emerges from the underlying mathematics.

Which mathematics?

(Lisp has its own math, for instance.)


Very fundamental ideas around computability, or total vs partial functions. There are some correctness properties that we can prove without running the underlying program, such that we can always accept or reject a given program. None of this is particular to lisp.


Fundamental ideas about computability include the model of the Turing machine, which has no notion of types, verification, or even functions whatsoever - there are just too many degrees of freedom.

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.


What on earth are you talking about?

> 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.


This crosses into incivility. That breaks the site guidelines. Please re-read them and don't do that when commenting here.

https://news.ycombinator.com/newsguidelines.html


I'm not sure how this is incivil?

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?


"What on earth are you talking about" is uncivil. Your comment didn't need that.

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.


Static typing enables faster feedback loops. The kind of thing Bret Victor raves about.

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.


> As for why we switch to dynamic typing as we scale up, some of it probably has to do with trust.

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.


> Most internal code at an organization is trusted,

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 is trust and typing seem orthogonal. E.g. I use static typing within one 'program' but 'dynamic' as soon as I split my program into two, even when the level of trust didn't change. Same thing when including my colleagues' code within my 'program' vs. calling it across program boundaries.

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?



Yeah, this was the "notable exception" I had in mind.


ML has metaprogramming? The only thing I know approaching that is template haskell, which is a weak form of metaprogramming (still powerful obviously).

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.


> ML has metaprogramming? The only thing I know approaching that is template haskell, which is a weak form of metaprogramming (still powerful obviously).

Of course, it has had it for a very long time (a decade at least?), see camlp4/camlp5.


How often is camlp4/5 used among the typical OCaml programmer compared to how often macros are used among typical Lisp programmers?

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...


> How easy is camlp4/5 metaprogramming to read and write compared to Lisp macros?

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.)


I've long desired a strongly-typed s-expression syntax that has the power of Haskell or Idris, but still looks and feels like the blank canvas that is Lisp.



Thanks!


“I am the Alan Kay in question”

Hah. Not everyone can be that cool.


It's funny, he can't even balance his parens in the first para.


Citizen Kaine is often called 'the best movie ever made'. If you watch it, it is arguable whether it is even minimally entertaining. I would say it is one of the most influential movies created along with Kurosawa movies. If I'm stuck on a plane I would rather watch the latest Transformers or sit in silence (given only those three options).

Lisp is arguably the most influential programming language, that doesn't mean it is objectively 'the best'.


I think the problem is that Citizen Kane was highly topical in its time, both in style and subject. If you didn't grow up watching newsreels, the parody of newsreels is not going to mean anything to you. If you aren't used to thinking about Hearst as a very powerful and controversial man, a lot of the scenes won't mean much. They make a joke about how he is starting the Spanish American War, but they don't really explain it. So I think it loses something without the context.


Do you really hold your own amusement to be the highest value?


You would rather watch Transformers then Citizen Kaine? Wat?


Actually I, too, would rather watch Transformer instead of Citizen Kaine. And I accept that later is a great film.


Attention spans are different now.


Personally I've found that it's the other way around: watching tedious, confusing, sterile CGI action sequences in contemporary movies like the Transformers series requires an attention span that I just don't have. I'd much rather watch Orson Welles just stare at a wall if I had to choose.


Whose and where?


I'm always blown away by how crisply is Alan Kay able to put complex thoughts into words. I feel that the level of clarity I can achieve on the scale of a sentence or two he can achieve on the scale of several paragraphs.


I'm similarly struck with how open the computer science frontier was in the 50s, 60s, and 70s.

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.


Counterpoint:

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 disagree. I found his meaning clear. The core of the essay is that thinking with the right abstractions provides greater boons to reasoning capacity than comparatively trivial differences in human IQ. To make things a bit more concrete, take any game; Go, Poker, Chess. If these were truly (approached as) combinatorial problems then humans could not stand a chance at them. But over time, people have learned patterns, compacted them and shared them such that the average level of play is what masterful players might have once struggled to reach.

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).


Godel's Incompleteness Theory states that within an axiomatic system, there are statements that are true that you can't prove true. You can only prove them true if you go up a level.

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.


LISP macros are formalized in LISP. You don't "go up a level" by using them. You go up a level by going into the human brain and thinking about your code in a way the computer doesn't.


Wow. That's a really interesting way of explaining macros.


I wonder if LISP and LSD encourage similar ways of thinking.


Based on my own experiences with both, I'd say: Yes. Although I'm sure you couldn't prove it mathematically (yet).


I’ve been using Clojure off and on for a few years but skipped learning about macros because I haven’t (yet) needed to write my own. Your comment makes me regret that decision.


"A complaint might be he left out what aspects of Lisp provided that combinatorial boost".

He said it right there: “unlimited programming in an eyeful” (the bottom half of page 13 in the Lisp 1.5 manual)


What is that a counterpoint to? He's not saying Lisp is 'better' than language X or that you should write your next world-conquering app in Lisp. He talks about ideas and inventions that can act as a sort of 'force multiplier' in thinking. In his analogies, he points out this applies to both fundamental innovations (Calculus) and innovations in representation (Heaviside formulation of Maxwell's equations). He argues that Lisp is that type of innovation in Computer Science.


>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.

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.

[1] https://news.ycombinator.com/item?id=15472204


Yes. It’s similar to the difference between a mechanic and an engineer: a mechanic figures out something works, but the engineer can then take the next step to discover/know how. If someone cannot articulate or identify advantages, then they are pushing unproven, possibly wrong “religious” beliefs like a “mechanic.” Zealous communinal reinforcement is political, Macaroni and silly when there are no convincing arguments other than “because it’s new” or “because.”


Agreed, and well said.

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?


Lisp's popularity came too early. Lisp really needed some relatively powerful machines to take advantage of it, and when it was actually popular most machines were way too weak for it. Now that machines are finally powerful enough to handle it, it's no longer popular, and it has no army of programmers using it as they are using some of the more popular languages like Java or Javascript.

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...


Well, Clojure seems popular.


Don't confuse lisp-as-an-executable-device-to-think-meta and lisp-as-one-of-programming-language-choices. Alan is talking the former.

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 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?

Couldn't you substitute calculus, real analysis, or abstract algebra for Lisp in this statement and make the same claim?


Like Steve Jobs he is amongst the group of people who could say anything anyway they like and some people will find a way to glorify it by a factor of a million.

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.


What is the sad admission here? That not everyone establishes 100% of their belief system from first principles, and instead adopts some of it empirically based on other reasoners' beliefs? I don't consider that a bug.

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.


That this happens is of course difficult to disprove. My only defense is that very often, I read things from acclaimed people and think "bullshit". I don't have any particular reason to like what Alan Kay says besides having read previously things from him that I liked.

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.


Honestly, I didn’t have any effing clue who Paul Graham is after reading half of his essays and description of Arc. Realized that he wasn’t yet another dev blogger only after someone mentioned on HN that HN is somewhat his. Also, it feels like Joel Spolsky was read in my environment long before stackoverflow.com existed (or became popular). Just one single data point against your hypothesis.


This is really well written and seems to agree with the traditional FP lore. I'm curious what led Alan Kay then to OOP in Smalltalk.


Alan Kay OOP is a lot closer to the Actor model that you see in languages like Erlang. To be overly reductive, its all message passing and not-exposed state.


Lisp programming has never never pure functional programming. It has been sometimes used to teach functional programming, but traditional Lisp's (Common Lisp as good example) has always been multi-paradigm language and object systems in Lisp's are fairly advanced.

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...


There are no "if" statements in Smalltalk. You call the"ifTrue:ifFalse" method on an instance of a Boolean, passing two lambdas for the respective cases. To this day I don't know what an "object/functional hybrid language" is, as Smalltalk was always that


OOP used to mean something quite different than it does today. Learn smalltalk, you will be surprised.


The Early History Of Smalltalk is a good discussion of this stuff: http://worrydream.com/EarlyHistoryOfSmalltalk/


A trivial but cute detail is how the author’s email address is specified in this 1993 paper:

kay2@apple.com.Internet#

A year or two later, there would be practically no other email systems than “Internet”.


"… the insight that everything we can describe can be represented by the recursive composition of a single kind of behavioral building block that hides its combination of state and process inside itself and can be dealt with only through the exchange of messages." - Alan Kay, The Early History of Smalltalk


Whatever Alan Kay thinks OOP is, it isn't what we think it is.


just fyi, Alan Kay coined the phrase Object Oriented Programming. So your statement, while true, should probably say what we call object oreinted programming should have a different name.


If you want to do OO Alan Kay style, you need late binding, metaobject protocol, and messages. The difference between dot.setX(10) and dot.moveToHorizontalPos(10) may seem irrelevant, but the other messing with internal state, other asking object to do something.

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.


He did popularly name what others (Nygaard and Dahl) invented, and for which they won ACM Turing Awards.

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'."


Simula can't be praised too highly, and it was a huge influence. But if you take the care to check, you will find out that what I termed "Object Oriented" was quite different from Simula in (what I thought were) important ways. And if you are looking for the earliest inventions of ideas like these, there are several that predate Simula (take a look at that HOPL II chapter mentioned above)


I certainly don't want to underemphasise the huge contribution 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.


> Whatever Alan Kay thinks OOP is, it isn't what we think it is

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.


Ralph Johnson (one of the GOF) stated there are three views of OOP: the Scandinavian view; the Mystical view; and the Software Engineering view.

> "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.




Consider applying for YC's W25 batch! Applications are open till Nov 12.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: