Alan Kay's Quora dialogues and the philosophical dilemma of Systems thinking

“The industry” is a very diverse set of actors, and then there are many actors who develop software outside of any industrial context. I doubt there are any “core principles of software engineering”. There are no core principles of matter engineering either. Electrical engineering works differently from civil engineering, and even within electrical engineering, there are enormous variations. Software is highly contextual, and so should be the engineering principles applied to it.

This also applies to the protection issues you discuss. Different contexts require different protection mechanisms, depending on how critical the software is and who the various actors are.

Again, it depends on the context and in particular on who the user is. Is it a user who has the competence and resources to inspect the whole code? Or a user who runs the code blindly? Is the code widely discussed and evaluated? Or so specialized that only ten people in the world have a chance of understanding it?

Context again. Smalltalk is a small world in which everything is explorable by a literate user. Android lives in a huge app universe in which mostly illiterate users need to make choices based on trust rather than understanding.

No. You can create an anonymous class in Smalltalk. All it takes is Class new. You don’t see much discussion of this because nobody has found really good uses for anonymous classes.

Not sure about that. We have multiple interconnected problems to solve. Attacking climate change requires massive coordinated changes of behavior around the globe. I don’t think we can get there without information and communication systems that people can trust. But it takes massive social changes as well, in particular reducing inequality.

2 Likes

Also, the tools in Smalltalk systems are very oriented to the named class hierarchy development style.

There’s nothing stopping someone from making a system based on anonymous classes or prototypes or exemplaries. It’s just not supported by the standard toolset.

The Squeak family even have ProtoObject which was at least partly intended to make this easier.

1 Like

Tool support in Smalltalk (also elsewhere, but particularly in Smalltalk) is an expression of what the community considers useful and/or good practices, so we are saying the same from two different perspectives.

1 Like

It’s been a while since I’ve been here, so sorry for the very late response, but I thought this answer was very on-target:

Yes, context is important, but what I would like is a form of software where the act of “running”, and therefore the trust/context required to authorise it, is not an all or nothing thing. A personal and shareable software system should be totally understandable, but you shouldn’t need to understand it all at the beginning. You’d create and share such software in small pieces and each piece would have very limited rights to a small part of your display or workspace. At worst, deliberate malware could, say, draw an upsetting picture in its own display window, or corrupt or leak the one file you have given it rights to edit.

It should not be possible (and this would be hard enforced in the design of the virtual machine / operating system that it runs under) for software - no matter where the operator obtains it - to escalate automation outside of its permission boundaries, or to impersonate another software component by, eg, creating a window that looks identical to the other one. And there should always be trusted automation available in your operating system to analyze and preview what untrusted software components will do. Not just a “run/don’t run” binary like antivirus systems, but more like debugging and programming environments which invite exploration.

“Running” such software wouldn’t be “handing over your house keys” in a one time startup operation, but would instead be a slow process of exploration and unfolding in which the operator is always in control, and can always reverse actions which the software takes that they don’t like.

Back in the 1980s, we accidentally had quite a few of these safety properties just by 1) not having always-online Internet, and b) having removable disk drives. A program couldn’t access any data without the operator loading the correct disk, and it couldn’t go online without the operator physically connecting the modem (and often manually providing a phone number). The sort of thing I’d like would be a really minimal operating system kernel which provides an equivalent level of “positive operator control to access of resources” as we had in 1980 using physical means.

1 Like

Hello! I would firstly like to say a huge thank you for making a archive of Alan Kay’s posts on Quora.

I like to check Alan Kay’s answers on Quora every so often, and when I searched today, I came across this page. It is definitely a pain browsing on Quora.

I also appreciate the topic of discussion that you have brought up, even though I may disagree with your argument. I will elaborate on what I think and my reasonings in a future post, when I have some more time to respond in-depth.

4 Likes

Thanks! And welcome to the forum.

I also appreciate the topic of discussion that you have brought up, even though I may disagree with your argument. I will elaborate on what I think and my reasonings in a future post, when I have some more time to respond in-depth.

I probably disagree with my argument, too, so go ahead! I have conflicting, incompatible thoughts and would like to tease them out.

My problem is partly that I lived through the 1990s, in which Object-Oriented Programming had become a weird semi-religious orthodoxy, yet nobody knew what it was. If you had to ask what it was, then you were Not Getting It and Probably Couldn’t Be Taught. But whatever it was, it was the Holy One True Answer To All Programming Problems. But every manufacturer implemented it differently, and none of them could connect.

And I lived through Microsoft buying into the Objects vision (in the form of Components and Compound Documents, which were slightly different, but Object-y), and shoving user-scriptable Object interfaces into Windows and Office. And the total security disaster that unfolded from that, when it turned out that many of those Object interfaces granted far too many permissions, so “macro viruses” - and being afraid to open any email - were the result. And we’re still there.

And at the same time, I also lived through the backlash against Objects (which by that time just meant “Java”) and the return of Databases and Functions. Because the entire Web was built on something (TCP/IP packets, DNS, Unix filesystems, text files, HTML/XML, JSON, SQL) which absolutely was not Objects, but was the opposite: “user-transparent raw data with a very strictly defined format”. CORBA, the annointed Object Management Group protocol of the day, also absolutely failed.

So it feels like I’ve lived through both the rise and fall of Objects, and then a post-Objects state that we’re in at the moment, where we have Objects still, but as one of zillions of incompatible theoretical frameworks for thinking about programming: functions, types, version control, packages, databases (SQL and noSQL), scripts, documents, graphs, VMs, containers, clouds, etc. And this wouldn’t be so bad except that every theoretical framework exists in multiple languages, the automation tools don’t span frameworks, and the joins between all these frameworks are where errors creep in. And where there are errors, there is, shortly afterwards, cyberwar and ransomware and a total collapse of first your site, and then the entire world. To counter this, Silicon Valley and corporate IT are leaning hard into Total Surveillance and banning compilers, which is another kind of dystopia. It feels like we’re now committed on a path down to global democratic collapse. With the wheels greased by massive security failure on the one hand, making self-hosting of software a legal risk, and cloud-based IT run by an unelected security elite on the other.

And as software engineers we seem to be not as bothered about this situation we’ve caused - and its root causes - as we should be.

But I like part of the Object vision: a) namespaces everywhere, b) being able to update your system in small pieces and not in one big recompile/reinstall/restart, c) being able to find out at runtime what kind of thing is that you’ve been passed and what functions you can run on it, and d) You literally can’t corrupt an Object’s memory or smash its stack, so you get runtime safety, a very good thing, as opposed to say Forth where every instruction is yikes, this could (and will if its malware) corrupt my entire RAM.

Things I don’t like about the Object vision: a) having all your data, even data passed to you by remote untrustworthy computers, be opaque Turing-complete executable binaries - yikes, nope! b) binding multiple-dispatch functions to only one of the multiple pieces of data that they belong to, c) brittleness of classes, because they add too much over-optimised behaviour that assumes a very particular context, up to and including the binary machine it’s running on, when all you actually need - and can reuse - is the data inside, and d) not being able to add new methods to an object, even though “I have a new function to run on a piece of old data” is such a common pattern to have.

(Pharo Smalltalk fixes that last one, with some kind of nonstandard extension that lets you fake up adding methods to someone else’s objects. Javascript also allows you to do this with “monkeypatching”. It’s obviously a thing people very much need, but the core Object vision very much does not endorse this. The Object is supposed to be in control of all messages sent to it, and the sender just works around that.)

Of course, Objects as we’ve had them since C++ and Java are not Objects as Alan Kay imagined them. So that’s why I’ve been reading Kay to find out what his actual vision was.

But what worries me a bit about Kay’s actual vision is that I think he is really leaning very very hard into my (a) “Yikes, nope!” scenario: I think he really really wants all data sent between distributed computers to be an entire sealed-box Turing machine. And these objects would have access to your machine context so they could, themselves, figure out what kind of system you have and how to represent themselves to you. And then have that be the lowest-level framework that everything else is built upon.

And in a Net where bad people exist who write bad software, and that software straight-up lies about what it will do, that would be utterly terrifying.

It’s not quite as terrifying as what we would have without some kind of Objects [1]: everyone running raw machine code against raw RAM with no memory management (the DuskOS vision!) But unrestricted Turing-completeness of all data, even with memory safety, is almost as bad.

[1] I actually mean “Processes” or “Modules” or “Capabilities” here - but the allure of the Object vision was that you could replace all of those older separation-of-authority concepts with “everything is just an Object, that’s it, all software is tiny virtual computers all the way down, just like the Internet”. But I’m not sure if everything can be. We didn’t build either the Net or the Web on Objects (we built some of the endpoint software with Objects, in RAM, temporarily, but we didn’t send those in-RAM Objects, as Objects, over the wire; we sent them as clearly defined system-independent dumb Data, with no classes or methods attached). Could we have made a truly Object Web? And in an age of pervasive malware, would it have made things better if we had – or much, much worse? Java and Flash in the browser were the closest we got, and what happened to them? And WASM is perhaps the next iteration of that… and is that going to be nice for us, or is it going to be all DRM and cryptominers? And to the extent that browsers and operating systems have sandboxed their downloadable/executable objects, they’ve done it by tying trust to the remote server’s domain name or publisher’s certificate, which increasingly locks out the local user from creating their own software and having their own computer trust it. Because they didn’t buy a certificate from a trillion dollar company in order to get permission to run a program they wrote.

So I’d like a vision of small pieces of data and software, yes, made of a very small number of basic concepts… but the software must, and this is absolutely non-negotiable, it must be passed between systems as data, not as opaque executable binary or a pointer to it. And a system must have the capacity to deconstruct the code of all software passed to it, to make sure that it absolutely will only do what is known to be within its authority to do. If a receiving system cannot prove what some incoming code will do, then that receiving system MUST block that code from running at all. Zero exceptions.

And this attitude of radical distrust – not “lol just trust the object to do whatever it thinks is cool, it’ll probably be ok, after all this is California and we’re all surfing buddies in a university, right?” – must be the foundation of any programming paradigm which can survive on the actual Internet made of human beings. But this evaluation mechanism must also be based purely on what the code itself is - not on a third-party gatekeeping from a corporation or government.

That’s my thinking so far, anyway. I’m not sure that it’s completely coherent though.

Regards, Nate

Edit: The Turing-completeness I’ve complained about here with Objects would be even worse in a pure Functional system, if we required that all data sent between software components be a Turing-complete opaque Function (like a Scheme closure, or a Lambda in a pure lambda-calculus system). Objects at least have one piece of non-fakeable metadata attached (the Class, Prototype or Interfaces) which arbitrary Functions don’t. But class-hierarchy-based Object systems try to squeeze too much meaning into this one piece of metadata - and Interfaces don’t give any guarantees at all about the behaviour of an Object, just what type of data its methods operate on.

I guess my worry is that Kay thinks that Objects can be improved by removing the metadata that they do have - moving away from Classes and the “Abstract Data Type” concept. I think we need much more metadata – but that metadata has to be honest, which means it has to be generated by a trusted local component - NOT by either a remote central broker or by the object itself, because both of those will lie to us. If we were very lucky, perhaps we could compose all object/functions entirely out of metadata, in a fashion a bit like dependent types or like Prolog predicates / Kanren relations.

Perhaps Kay is thinking the same way. I hope he is. But we need to be thinking of all networks and distributed systems – including, especially, large centralised “authorities” – as fundamentally warzones where software objects employ active hostility and deception and not as collaborative sets of trusted colleagues whose expertise should be deferred to. And design accordingly.

Edit 2: A large chunk of my concern is around that Objects buzzword, “Behavior”. Behavior is both too system-dependent (too fragile/optimised, can’t travel between systems, locks valuable user data into system/app specific silos), and too unpredictable/dangerous (Turing-complete, can lie about itself when queried, very hard to automatically analyze). I don’t need my fundamental data/software components to behave (do random unpredictable things which can vary at runtime). I need them to be (have strictly known properties in all possible contexts). Ok, perhaps that’s an overstatement, in small and experimental contexts - but even there, known limits on behavior have to come before behavior. Arbitrary behavior is cyberwar.

Edit 3: Alan is of course a lot more nuanced about this, and sees safety of execution as part of what he wanted from Objects

One of the things we realized at Parc was that it would be a very good idea to implement as much of John’s “situations” and “fluents” as possible, even if the histories were not kept very long. For example, this would allow “real objects” to be world-lines of their stable states and they could get to their next stable state in a completely functional manner. In the Strachey sense, they would be “viewing themselves” with no race conditions to get their next version.

This would also be good for the multiple viewing we were starting to use. You really
only want views to be allowed on stable objects (/relationships) and this can be done
by restricting viewing to already computed “situational layers”.

Parc was also experimenting with “UNDO” and the larger community was starting to
look at “parallel possible worlds reasoning”. The acts of programming itself also wanted to be in terms of “histories and versions” and systems should be able to be rolled back to previous versions (including “values”, not just code). cf Interlisp, and especially the PIE system (done in Smalltalk by Goldstein and Bobrow).

3 Likes

I’ve been looking at Kay’s older Smalltalks to see what kind of thing he had in mind. Smalltalk-76 and later were optimized versions, he wanted to restart and it was only Dan Ingalls demonstrating ST76 that convinced him to continue. 76 and earlier are quite interesting as is why certain things were done. e.g. Inheritance was apparently added for code reuse. Nothing more.

A lot of what people consider to be “Objects” is not in there.

3 Likes

Yes, Kay today thinks of inheritance as a mistake, and that seems sensible given how many problems there are with it, up to and including it being almost impossible to actually define a “subclass” relation in the presense of overridable methods. The Alto was a really underpowered machine - about the size of two Commodore 64s. Code reuse was needed then, but maybe not today.

I wonder what kind of object system we might be able to create if we started with something like an early Smalltalk, then went object mutation, but with no inheritance. What might the GUI code look like? Or, what if no mutation even? Could we get away with that?

The Alto microcode 10x faster than RAM so it didn’t need interrupts was really clever, and I also love what Kay says about the Burroughs B5000 architecture, which had address-level capabilities. I think all minimal VMs need something like that. Raw integer address pointers, as in the current wave of retro Forth engines, are insanely dangerous, I’m sorry. We shouldn’t be building any of those in 2024, even if they’re easy to implement. A tiny Lisp engine would be so much better, because conses have unforgeable addresses and they’re super easy to allocate and reuse. Forth is attractive because it’s so fast, but integer pointers, on a machine with real-world RAM sizes and any amount of persistent disk storage, are just going to get us destroyed.

I really like most of what Kay says, so the “Objects as executable blobs without metadata” idea that I complain about is probably a strawman that isn’t at all what he is proposing as “Real Objects”. I’m still not really sure exactly what he is proposing. But eg, he says this:

I think partly what is needed in analogy to deal with the current difficulties is the next
logical stage of “the book” as it could be using the computer medium to carry it. This
is a larger discussion that can be had here, but consider that “a book” today could
teach reading itself, could help the reading of it, could be much much more than just
words and pictures — or movies and sounds — but instead an entire learning
environment. This does not solve enough motivation problems to work, but could
make the difference in society-enforced schooling by augmenting the adults who are
trying to help — “helping the helpers”.

Ok this is a great vision, sure. The “Dynabook”, but for real. Let’s get back on track and make that 60s thing happen. But. “A learning environment” that is “much more than just words and pictures” suggests that there’s going to be shareable executable code objects involved. That’s going to turn out exactly like Flash, ActiveX and Java in the browser unless we do it right. But do we yet know what it was exactly that Flash, ActiveX and Java did wrong?

How do we make those kind of objects - that is, if they’re authored locally by kids and shared sideways around a network , and not gated through a central corporate domain name or appstore - as safe as text?

Kay again:

What does Alan think of this remarkable initiative?

Classes are a pretty top-down way to do this, and I’ve never liked them
because I think that design is aided by being able to make things before
having to decide just exactly what they are. Prototypes (perhaps with
delegation) are at another extreme (in some languages they just become the
root of the links, in others they are partly copied and partly linked) — they’ve
always felt “too wiggly” to me. One of the ideas we talked about at Parc was
“Exemplars”, which were kind of “sample or ‘typical’ objects” that would have
more expressive power than a class, and more stability than a prototype. For
example, in Smalltalk “Class Document” is not very explanatory, but more like
a framework. “Exemplar Document” would be instantiated to make a typical
document, and could also serve as a framework. Note that a typical “part” in
an exemplar would likely be different from a default or a “master version” (all
these are probably needed).

Hmm.

What’s a uni-class?

My version of “object-oriented” from 1966 on revolved around the active entities in a
working system — I thought of these as being “full fledged computers” on a network
and communicating via messages (this paralleled and was inspired in part by ARPA’s
plans to do this with physical computers). With “virtual computers” I realized that you
could do the neat thing of making everything — including the virtual networking and
the messages, etc., and the stuff all this was made from — with “virtual computers” (all the way down).

Yeah, but, now assume that some large percentage of all of those “virtual computers” are absolutely evil and hell-bent on your personal destruction. Because they will be. So now, given that knowledge, what protocol/language/kernel design minimises the harm they can cause?

If you can have lots of classes — and modern languages can — it is possible to use
some of the existing mechanisms to make a more bottom up experimentation
environment that allows a lot of flexibility, is still using real objects — not data
structures with attached procedures — and that — in a late-bound language like
Smalltalk — can then be “elevated” (“beatified”) into stronger abstractions as the
systems design becomes more clear

Yes, but but but. This is all assuming that you, or a close-knit trusted team, are writing all those objects yourself. And you won’t be. “Data structures with attached procedures” at least have the advantage that the procedures can be yours and attached to your local trusted computer, while the data structures, which some percentage of will still be 100% evil, are now somewhat less harmful because they can’t execute code. You can execute your code against that evil data, and be reasonably unscathed as long as your code is correct. But if that evil data also comes with its own evil code attached? You may be in big trouble. Those “real objects” are not going to be “beatified” by being strengthened, they’re gonna be… the other thing.

That’s the question about 2024’s Internet that I want Kay’s vision (not necessarily Kay himself) to answer.

I still don’t really understand why Kay thinks that calling objects he doesn’t like “data structures with attached procedures” (elsewhere “abstract data types”) is a burn. But he really does think it’s a burn.

Do I lose something when learning OOP in Ruby instead of C#/Java?

So: my advice is try to avoid “learning OOP” via a language. “Real OOP” is not about
making abstract data types, setters, or most of the things that are typically done in
them. “Real OOP” is about dynamic systems, and the way to learn it is to start
designing and building them and gradually build up a feeling for scaling,
modularization, intercommunications, etc. Then you will be able to choose how to use
the (too many) degrees of freedom available in almost all programming languages.

Very good advice… but it’s that “real OOP is dynamic systems” bit which, like the word “behavior”, makes me very uneasy, and is what I’m referencing in the title of this thread.

I actively do not want evil code in my computer (and it’s going to get there, if only as text in an email) to be a dynamic system. I want it to be extremely static.

Edit: An idea perhaps of what Kay is thinking about might come from the “Vivarium” project he led at Apple from 1986. And again: cool vision, but also, in the presence of active malware, hmmmmmm…

https://web.archive.org/web/20080302033014/http://www.beanblossom.in.us/larryy/VivHist.html

In the process of trying to meet the many Vivarium goals, the Playground team has evolved a new concept of programming, that may be referred to as event-oriented. It is thought that this programming model may be as large and important a step beyond object-oriented programming as that model was beyond procedural programming. Object-oriented programming provided a new way of thinking about the solution to many classes of problems, and through data encapsulation and method inheritance offered an extensibility that was almost impossible to achieve with traditional, procedural programming styles. Even so, the introduction of a new class, a new functionality, into an existing set of classes and objects requires a programmer to touch some number, possibly all, of the other classes in the system in order for the new class to be able to take part in the global network of communication and computation. This is due to the “pushing” nature of communications in such systems; i.e., objects are only activated when some other object explicitly pushes a message onto the system-maintained message list addressed to that object to be activated. A new object/class in such a system will lie fallow since none of the existing objects knows of its existence, nor how to communicate with it. The event-oriented system envisioned for Playground is a “pulling” type of system, in which objects can be directed to notice their environment; i.e., they can pull messages and information from the available system-maintained list at their own discretion. So a new object, which we might now begin to refer to as an agent, can be made to notice various other agents and events within the environment, and can begin to play a role in the global communication and computation immediately upon its introduction into this information ecology.

Nate

2 Likes

I believe you’ve mentioned this concern a few times, but isn’t this an ecosystem problem rather than an object problem?

I’m as suspect about Kay’s biological analogies as I am John von Neumann’s brain analogies - but I think that Kay’s objects cannot be concerned with an issue as they are envisioned.

Maybe it is a bad idea. But if we’re looking for “what Kay meant,” I think we just need to accept the fact that all objects would live in a larger ecosystem with “good” and “bad” actors. Kay didn’t talk much about this to my knowledge, but he does believe that objects could someday communicate even without a predefined protocol using something called an ambassador.

I just have to assume that this ambassador is like all ambassadors - the intentions are not always clear.

Such management would have to happen at the ecosystem level. Not at the object level. In Kay’s mind, I think this works as it does in a biological system. And the discussion is maybe better suited for the Christopher Alexander thread. :wink:

I appreciate the effort on this thread to look at what Kay actually made (e.g. what’s actually in Smalltalk or Squeak), rather than on what Kay speculated could be made (e.g. what’s a Dynabook).

2 Likes

Yes, it’s an ecosystem problem, but a toxic/hostile ecosystem is always going to be there. And we now know just how hostile it is. So now we need ways of setting up membranes, basically, to filter out the nasty parts of the ecosystem and set up trust parameters. Any new alternative to the Web we imagine has to start from the question of “how will this be abused at scale? and how do we build it so abuse is at least a little hard? what happens if a user has a desktop containing multiple live objects and some of those objects (ignoring for the moment just how they got there) are absolutely evil (like, nation-state backed ransomware/cyberwar evil - as, say, any random PDF attached to any random email in your inbox might be today)? since we’re designing an object protocol, how can we best minimise the damage that the most evil objects imaginable on your desktop can do?”

In the early days of ARPA and PARC I think the issue was less around trust and more around capability. Just being a part of the ARPA research community was already a big selective membrane - so this filter generated people like Richard Stallman who advocated for massive openness of source code, Ted Nelson and massive hypertext “intertwingling” and “transclusion”, and (a little later but still PARC-like) Ward Cunningham and the original open-world-writing, no-user-logins Wiki concept.

Now we’ve got lots of Internet capability, but much of that capability has turned hostile and predatory. Including all of: ransomware gangs, cryptocurrency scammers, influencers, militaries, adware/DRM, and venture capital. By their own metrics, of course, all of these predatory dynamic systems are doing the “right” thing! They’re seeking their own benefit and maximising their utility, just like microbes. But this is of no help to us, the users. To us they’re all attackers to be defended against. And to them, we’re just food.

So yes, we’ve successfully created a “biological” environment, like Kay wanted, and now the goal is no longer bootstrapping that ecosystem, but surviving it.

And an object is a membrane, so one would think that objects and the encapsulation principle (“no raw data for you! only use the Turing-complete API and get what I give you!”) should be a universal, composable technique.

But maybe it isn’t.

What worries me is that fundamentally, encapsulated objects as building blocks might not actually compose in the presence of a hostile ecosystem of such objects, massively shared among users (ie, in the natural state that the Internet is and will be for the forseeable future).

Currently, we’re mitigating the hostility of the Internet ecosystem by reducing the “massively shared” part way, way down from what Kay envisaged. We’re chunking objects into fairly large parts: “operating systems” and “applications” on the client, and on the server side, heavily sandboxed “domains” and “web pages”. We still have quite a strict client/server split. And on the server side, we’re routing everything through giant cloud companies, and on the client side we’re also routing these large downloaded executable chunks through heavy surveillance systems like Microsoft Defender and SSL-decrypting firewalls. This is just to tread water, to stop one accidental click on a corporate email from executing a ransomware binary that destroys the whole domain. This mitigation technique is very heavyweight and it destroys privacy.

If we wanted to get more “Alan Kay object-y”, or “Ted Nelson hypertext-y” and have finegrained, persistent, process-like, Turing-complete live objects from all across the Internet just strewn all around our desktops like live grenades - without even the concept of a “web page inside a DNS domain plus SSL certificate” separating out their local access rights and giving a bit of a visual delimiter around them on the UI side - then we would need our per-object security mitigation to be a lot more bulletproof than it currently is. That’s what I’m thinking about.

If encapsulation doesn’t compose in such a way to give us security guarantees (and I fear it doesn’t), then we need to understand why it doesn’t, and search for a primitive technique that does compose securely.

My feeling currently is mixed. I like encapsulation, generally. It requires less trust than handing a function your entire RAM or stack. You’re only exchanging limited amounts of shared-nothing data.

But encapsulation is a two-sided membrane. It protects the trusted local system from the untrusted foreign object, which is great – but it also hides the untrusted foreign object from the trusted local system, which is not great. So I think encapsulation needs to be augmented with some kind of build-locally-from-source, chain-of-trust model. To be actually trusted, an object has to be not only given to us by a trusted object, but by an object with the capability of making transitive trust decisions… something like a compiler, bytecode verifier, or like an inspecting firewall… that can analyze the foreign object and understand exactly what it is doing. Not just “what API it claims to support”, but rather what it will actually do when passed a message, and more specifically, what it can’t do. Will this object claiming to be an onscreen keyboard or an implementation of “FastZipFileDecoder”, actually silently send all your data to either putin dot ru or cybercommand dot mil? How can we know it can’t? Not just hope, but know?

I like “data” as a primitive root-of-trust but really all that “data” is, is a system-provided, non-overrideable, stable, reasonably trusted API (ie, things like “Byte”, “String”, “Packet”, “File”). By their nature, I think there will be a very small number of such APIs and their trusted local implementations, compared to the number and variety of general, user-created, shareable objects/functions that are built locally out of those trusted local objects.

The questions I have maybe come down to two at this stage:

  1. “what would be a good, small, set of such globally defined and locally implemented APIs?”

  2. “what’s the absolute minimum that a language/kernel/protocol/VM needs in order to bootstrap a chain of trust for objects/functions?”

with 1 being maybe hardish, and 2 maybe just being “a trusted local system kernel object which builds all objects/functions, which you can pass an object/function to, and which looks up in a fast local weak hash table and says yep, I built that, and it has these properties”. To do this, objects would probably have to be immutable or at least have an immutable part (like a class, but not limited to that).

If we don’t care about speed and garbage collection (ie weak hashes), then perhaps we can get by with a VM/kernel which just implements a) pointers (like Lisp/Scheme conses) which aren’t raw integers so can’t be faked, b) Scheme-like closures which can be created, and then called but not viewed, c) immutability for closures - Scheme probably gives us this automatically and d) a reference-equality function for closures (which you probably need anyway). a implements encapsulation, and b and c allow implementing (even if inefficiently) the “compiler / builder / oracle” which builds objects, stores their references, and securely answers questions about the objects it built. But if we’re gonna be using d a lot, and we will, we probably want it to be something like an object ID hash and not just running an equality test for every single closure in the system. (We might be able to make do with a “less than / greater than” test for object IDs / memory addresses - so that we can at least sort addresses/objects into trees - but those comparison operations might still somehow be a security risk if the object IDs aren’t randomised.)

I’m wondering if we could even get to this minimal acceptably secure level with just a stack-based machine that uses a cons store with opaque addresses and functions. Would still probably need garbage collection, but we could maybe make it quite small. Type annotations on the stack might be required (as in StrongForth), and how to bootstrap types without allowing any typecasting at all might be hard.

2 Likes

For encapsulation I’ve been meaning to study E-lang’s Vats, Jonathan Rees “A Security Kernel Based on the Lambda-Calculus” and maybe even the Smalltalk image for ideas.

They have the right “feel” to me but will need a lot more thought.

Well I’d hope this web you’re imagining leaves a lot of room for for disagreement.

All the way back at the dawn of multi-user systems we had two approaches: the Compatible Time Sharing System and the Incompatible Time Sharing System.

I’d argue they’re equally important. The ITS legacy can be seen in open Wikis, open source, and open systems like Emacs. All of which are vulnerable to attack.

But I’m not convinced the CTS multi-user system model is terribly secure and it comes at significant cost.

The failure of CTS-type systems is why I suppose you start reaching for object primitives. Perhaps we could help objects thrive in a hostile environment?

But these primitives seem destined to suffer from the same root-of-trust issues of every complex system. If an object calls you, it matters who the object is and what time the call happened. Stated along another dimension, it’s a matter of identity and reputation.

Systems tend to run a paradigm between “open” and “closed”. Isn’t what you propose that somehow avoids this tension altogether using mathematical guarantees? The most well-funded approach to this are today’s “trustless systems” in crypto. I would be happy to hear about alternatives.

2 Likes

Well I’d hope this web you’re imagining leaves a lot of room for for disagreement.

Yes, I think preserving the right to disagree is a fundamental requirement in distributed software systems, and I’m worried that our current Internet architecture is taking that away, with dire consequences.

I’m very wary of the current trend of piping everything through centralized security just to get a bare minimum of “probably won’t get ransomwared today, but might get crowdstriked instead”.

I’d instead like to see distributed security where the fundamental function-call or message-send primitives are “secure” in a very minimal sense of “can’t break things it doesn’t own”. We don’t even have this level of minimal security on most desktops today. Hence the ransomware plague and the plague, in response, of centralized surveillance systems.

All the way back at the dawn of multi-user systems we had two approaches: the Compatible Time Sharing System and the Incompatible Time Sharing System.

Interesting! I hadn’t thought of those two systems being the root of two different approaches - mostly because I’m not old enough to have used either of them.

The failure of CTS-type systems is why I suppose you start reaching for object primitives.

Nope, I’m making no reference to “the failure of CTS” because I have no idea what CTS was.

I’m “reaching for object primitives” because I admire Alan Kay’s original Dynabook vision and he seems to still think that objects (as he envisaged them, not as they actually are today) are part of that vision. And this thread is me trying to grapple with and critique what Kay’s vision actually is, and whether I agree with it or not.

If an object calls you, it matters who the object is and what time the call happened.

I’m not sure that it does?

Replace “object” with “function” (because Scheme showed that that’s all an object actually is: or at least, that functions give a reasonable approximation of the simplest case of objects, if not all of the metadata that goes with Smalltalk objects), and ask yourself, does it make sense to say of a software system that “if a function calls another function, it matters ‘who’ the calling function is and what time the function call it happened?”

Well, I suppose it always matters ‘what’ a called function is: hopefully a compiler, an initial namespace, and access to the source code of your system, will get you that. I much prefer interpreted, dynamic, “always online” systems over compiled “offline batch mode” ones, but even there I admit that there’s gotta be an initial set of system-provided functions and libraries. That’s the root of trust. I’m happy to assume a runtime and a system library as a starting point, as long as both are small, local, and able to be examined by the user.

The identity of the calling function should not matter at all. Only that the calling function was able somehow to get an unforgeable reference to the called function. The act of possessing an unforgeable reference should be all that’s needed to establish the “right” to call a function. (Ie, I find the “capabilities” argument persuasive.)

The ‘time’ a function is called only matters in the sense of an ‘ordering’: and only if the called function contains references to variables that can be modified by previous function invocations. The called function itself should not be passed the time of the call as a parameter; that’s none of its business.

(I suppose if time - or any other changing dynamic state not passed as a parameter - is absolutely important to the operation of a function then it can be passed the right to read the system real-time clock when it is created. There would need to be some way of marking functions which contain such state, so that callers know that they are not a pure function of their parameters and eg can’t be cached).

At least, that’s how I see the minimal requirements for security. We could construct much more complicated “security” primitives over the top of these, but the core should be very simple and basically just “unforgeable reference, data record of a specified shape, and function call - where a function can be unforgeably linked to a metadata record containing its source code, but only if you possess a reference to that record”. Something like that.

One of the problems of our current crop of operating systems (including Windows but also Linux) is that all processes come with the entire “filesystem” attached as global mutable state. That’s much more state than most processes need, and a process is also much bigger and has much more shared state (ie its entire mapped RAM) than a function needs. A lot of our security pain comes from all this shared global mutable state: RAM within a process, and filesystem within a machine. We probably still need something like a filesystem, but the API to it needs to be much more fine-grained than the POSIX model. Shared RAM, we should long ago have moved from C “just index RAM with an integer lol” to something like the Lisp cons cell or Smalltalk object model. Not necessarily all the rest of the Smalltalk object doctrine - but at least memory protection at the level of individual data records.

1 Like

Here’s Alan Kay from about 4 years ago, coming on strong with his version of Objects which doubles and triples down on encapsulation and data hiding. I find myself strongly in disagreement with him, yet I keep wondering why. Surely he’s the expert and must be seeing something that I’m not? But his idea really bugs me.

If you use setters, you are not really working with objects, but glorified data structures. This use of objects as “Abstract Data Types” is not in the spirit of Object Oriented Programming and Design. This misuse is one of the biggest misconceptions about what OOP is all about — and it also removes much of the power of organizing modules whose contents are sealed off from the outside.

One way to think about “objects” is that each is an idea that holds possibly useful behaviors that can be requested (not ordered). So they are like servers with differential privileges. Since there are almost always many more ways to accomplish concepts than there are concepts — think of the idea of sorting” vs the many ways to do sorting — it makes great sense to separate the “language of concepts/behaviors” from the ways to accomplish the concepts/behaviors.

This allows many kinds of scaling and reformulations to be done both safely, and while the larger system is running (hint: your systems design is poor if you have to stop it to fix it or change it).

Another way to look at this is that “data” in most imperative and static senses of the word needs to go away — it is much too fragile and exposed.

You can request services, and some of these might be as simple as asking for a
particular value. If the server is representing a “person” then you might request “date of birth” and get a date object as a reply. If you request “age”, you would expect to get a duration object back. In the first case the date object might be held directly internally (probably not), and in the second case, the duration object is the result of an
on the fly calculation (and which might be “continuous”).

A more sophisticated use of objects would be to have a “goal pool” of things the larger system needs done, and the server objects could be constantly looking at the goal pool to find things to do. (This is a kind of “Publish and Subscribe” — or Linda — type process.)

Like. some of these ideas area really good. But, “Data needs to go away, it’s too fragile”? Yikes! Nope! I think the opposite is true!

Can two opposing ideas both be right, though?

1 Like

Here’s an interesting take on Kay’s “we should get rid of data” thesis, commentor “madhadron” on March 18, 2019, Hacker News:

4 Likes

Well back to my original claim:

If an object calls you, it matters who the object is and what time the call happened.

Indeed, callsite doesn’t matter. Unless security matters. Then it matters “who” it is. Could be as simple as “an acceptable ID” (unforgeable reference) or “something that has the correct key.”

Time matters in ordering, sure. But that’s not the caller’s problem nor the receiver’s problem. That’s some supervisor’s problem.

Time matters in a world of object mutation, though. Time does not matter in a world of pure functions. But we’re talking about OOP… so I thought that was pretty out of scope.

I agree that time is very important.

I don’t personally think that pure functions are out of scope for a discussion of OOP, because I believe Alan Kay is on record as saying that his concept of “real” OOP does not necessarily involve mutation any more than it involves inheritance. One of the several extremely confusing things about trying to decipher “what Alan Kay thinks”.

I could be wrong on this. I’ll search his Quora dialogues to check.

Edit: This is probably the reference I’m looking for: Why is functional programming seen as the opposite of OOP rather than an addition to it? - Quora

Alan seems to be thinking in terms of simulating time through some kind of wrapper or supervisory program (perhaps like Functional Reactive Programming), which stores multiple histories and can be simulated forward and rolled back, rather than having objects immediately mutating themselves. I like this a lot, and it’s very much not something we reliably see in OOP today.

I guess this could be seen as the “time of the call” mattering, but I think of it more as that the object would be generating new versions of itself (potentially keeping old versions in a history) and each of those versions would be a different thing. The system framework in which the objects are embedded would have to sort out which version of an object a caller gets - and that might depend on time, but it might also depend (as in Git) on a recorded history or simulated projection and “where” you were in that simulation rather than “when”.

But yes, binding the call-site reference of objects to the appropriate current version - whether in time or in simulation space - does become a non-trivial problem with a lot of sharp edges. The “objects are just pure-functional Scheme closures, so only the object itself matters, not the caller/callsite, so capabilities can just be references” approach I’m suggesting hides a lot of potential complexity in that wrapping framework that’s probably very important.

And yet, I keep thinking that the core of a messaging/evaluation system has to be very simple. If it’s not simple down at that most basic level, we’re going to get into exploding exponential complexity when we build on it. That’s why I think time is something that we should try to keep out of that basic level if we can at all help it.

He might be referring to how Actors replace themselves instead of mutating themselves?