53

I have been reading The Early History of Smalltalk and there are a few mentions of "assignment" which make me question my understanding of its meaning:

Though OOP came from many motivations, two were central. The large scale one was to find a better module scheme for complex systems involving hiding of details, and the small scale one was to find a more flexible version of assignment, and then to try to eliminate it altogether.

(from 1960-66--Early OOP and other formative ideas of the sixties, Section I)

What I got from Simula was that you could now replace bindings and assignment with goals. The last thing you wanted any programmer to do is mess with internal state even if presented figuratively. Instead, the objects should be presented as site of higher level behaviors more appropriate for use as dynamic components. (...) It is unfortunate that much of what is called "object-oriented programming" today is simply old style programming with fancier constructs. Many programs are loaded with "assignment-style" operations now done by more expensive attached procedures.

(from "Object-oriented" Style, Section IV)

Am I correct in interpreting the intent as being that objects are meant to be façades and any method (or "message") whose purpose is to set an instance variable on an object (i.e. an "assignment") is defeating the purpose? This interpretation appears to be supported by two later statements in Section IV:

Four techniques used together--persistent state, polymorphism, instantiation, and methods-as-goals for the object--account for much of the power. None of these require an "object-oriented language" to be employed--ALGOL 68 can almost be turned to this style--and OOPL merely focuses the designer's mind in a particular fruitful direction. However, doing encapsulation right is a commitment not just to abstraction of state, but to eliminate state oriented metaphors from programming.

...and:

Assignment statements--even abstract ones--express very low-level goals, and more of them will be needed to get anything done. Generally, we don't want the programmer to be messing around with state, whether simulated or not.

Would it be fair to say that opaque, immutable instances are being encouraged here? Or is it simply direct state changes that are discouraged? For example, if I have a BankAccount class, it's OK to have GetBalance, Deposit and Withdraw instance methods/messages; just make sure there isn't a SetBalance instance method/message?

2 Answers 2

98

The basic idea (influenced by Sketchpad) is that most variables/values are in dynamic -relationships- with each other (maintained by the interior of the object), so being able to directly reset a value from the outside is dangerous. Because (in Smalltalk anyway) there is at least a setter method required, this allows the possibility of an outside setting action to be mediated by the internal method to maintain the desired interrelationships. But most people who use setters simply use them to simulate direct assignments to interior variables, and this violates the spirit and intent of real OOP.

But objects do have "world lines" of changes in time. This can be thought of as a -history- of versions of the object in which the -relationships- are in accord. There are no race conditions in this scheme ... an object is only visible when it is stable and no longer computing. This is like a two phase clock in HW. (Idea from Strachey, and in a different from by McCarthy, and influenced by Lucid.)

Best wishes,

Alan Kay

4
  • 2
    @alan-kay: Thank you! May I have your permission to quote this in my thesis? Jun 3, 2011 at 12:51
  • 2
    Controlling side effect, or knowing how to control side effect as been the holly grail for lot of languages. But it seems that none have found it yet. :)
    – mathk
    Jun 8, 2011 at 21:30
  • @OlivierDagenais though I'm sure Alan would be more than happy (he seems like a pretty awesome guy), SE answers are CC licensed, so sourcing SE questions and answers is perfectly legit. Mar 18, 2015 at 12:02
  • Two phase clock? Is this the sort of like the "observer" pattern and dataflow pattern like in Excel or in React.JS where the objects propagate any changes along to keep constraints.
    – aoeu256
    Aug 28, 2019 at 18:56
22

I realize Alan already answered this question, and so it might seem like it is pointless to reply further. However, Alan did not answer every question you had.

In particular:

Would it be fair to say that opaque, immutable instances are being encouraged here? Or is it simply direct state changes that are discouraged? For example, if I have a BankAccount class, it's OK to have GetBalance, Deposit and Withdraw instance methods/messages; just make sure there isn't a SetBalance instance method/message?

The answer here is that you are not using higher-order behavior to structure your program. Real world financial services systems should not have a Deposit method on a BankAccount class, because that is not at all how banks worked before the invention of computers! When ATMs were invented, they had to literally automate what a Teller did at the bank. The role of a Teller would be to notify customers about the status of their account. To do this, the customer is allowed to interact with the Teller in only a few ways, such as passing a Deposit Slip to the Teller.

By directly reifying these objects - Teller, Deposit Slip, etc. - the problem domain is structured according to the messages passed by entities in the system.

An Account itself plays a role - the idea of an Account literally means an account of financial inflows and outflows in relation to an Asset, Liability, Income, or Expense. An Accounting system, or Accountant, records, retains and reproduces these flows and tells you the account's financial position at a point in time. The most recent report by the Teller can be thought of as "right now", but not really: It is really the financial position as described by the Accountant at a point in time. It just has the illusion of being "right now" when you go to the bank, because generally you are the only one authorized to make payments. This was especially true 100 years ago, but today many people have automated payments, and the account can change while you are physically at the bank holding a Balance Slip - because the Balance Slip only tells you about the point-in-time it was timestamped.

Why is this important? Well, ask yourself what needs to be done to record a transaction:

The Customer has their own internal audit log of everything they have done, including receipts from the Bank. Likewise, the Bank keeps their own internal audit log of everything they have done. The Bank always does double-entry accounting, meaning they record transactions in the General Ledger and Balance Sheet. This allows the Bank to perform reconciliation and make sure that there are no spurious entries when they close their books for a given financial period (daily, weekly, monthly, quarterly, yearly, bi-annually, whatever). This also suggests that the record for what gets recorded should be idempotent. Meaning, if we were to write a program to list all the unique transactions, we could do so even if spurious duplicates were in our internal audit log, because we embedded idempotent transaction identifiers into the logging messages.

Given the ability for automated payments to debit and credit your account, it seems to make sense that the Accountant could also do forecasting for you. This was the realization of the impact that computers could have on accounting systems. Accordingly, somebody invented an accounting system scheme called Resources-Events-Agents that was more inline with not just looking at the past, but also looking at the future and estimating cash flows at a finer granularity than before. Essentially, REA is just more metadata than classical accounting systems had, allowing for better reporting and business analysis. For example, "value chain" analysis and "supply chain" analysis are not things easy to do with classical accounting.

Likewise, Agoric Computing or Smart Contracts brings ideas from market mechanisms to computing. It is important that when you provide a Deposit Slip you also provide a Check or Purse to deposit. Since there is a lead time between receiving the Check and it actually getting into your Account, you need a secure way to manage currency. As it turns out, object capabilities are a natural way to achieve distributed secure currency. They can be used to ensure that Alice doesn't cheat Bob by withdrawing all her funds after she wrote Bob that check.

2
  • Thanks for dispelling OOP’s all-too-common BankAccount toy example.
    – akuhn
    Nov 5, 2012 at 7:44
  • While overall your answer is excellent (far too few people understand that an account is not a balance but a list of transactions, so thank you for that), you don't get double-entry accounting right. The point of double-entry is that every debit or credit to an account (i.e., list of transactions) has corresponding credits or debits to other accounts to match things up. E.g., if you debit a customer for ¥108 (i.e., the customer gave you that much money), you credit a revenue account by ¥100 and your "tax owed" account by ¥8 to match that debit and show where the money goes (or should go).
    – cjs
    Apr 22, 2016 at 9:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.