8 ms·
Alan Kay and Missing Messages
- Ovid 7y agoThis is the original discussion: https://news.ycombinator.com/item?id=19968496 https://news.ycombinator.com/item?id=19968496
- tyingq 7y agoHaving worked in healthcare IT, I'm less astonished than I should be about approximate matches and score thresholds being used to confirm "same entity" for pharma data. What could go wrong? (Not knocking the author...the practice is unfortunately needed because providers won't provide good data)
- Ovid 7y agoSorry, I wasn't clear. It wasn't quite like that. It was to help the pharmaceuticals work together to "pool" talented researchers who had the required training to proceed in the clinical trials. If a bunch of companies don't share this data, they drive up their costs tremendously in trying to find and recruit qualified doctors for their trials, pay to train them, and then have them conduct studies, only to find out that the doctor in question doesn't recruit any candidates themselves, or fails to report results. If, however, they pool their resources, they can find qualified doctors who have already taken the training and are known to participate well in trials. They can then correlate that with populations who might benefit from the drugs in question and save a fortune (and possibly many lives), by reducing the cost-to-market. The matching algorithm was actually very strong, but there was still a human step, from the pharmaceutical companies, to verify that these were the researchers they were seeking.
- tyingq 7y agoThat's helpful, though I can confirm the same sort of process is used in areas where it matters more.
- simion314 7y agoI personally did not understand the solution proposed here, I would like to see an actual solution to the problem that was raised.
- zeveb 7y agoThe idea is that rather than try/catch (which tries to do something and can report low-level failures but cannot resume them) one uses something more like Lisp's condition system. In a condition system, signalling an error preserves the stack, and it means for example that a low-level loop can detect an error (say, a poorly-formatted CSV cell), signal that to higher-level code, the high-level code can make a decision (e.g. to replace the contents of the cell or skip the cell or send an email to the CEO with the contents of the cell and wait for his reply) and the low-level loop can continue processing as if there had never been a problem at all. There's a really great explanation in Practical Common Lisp: http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html http://gigamonkeys.com/book/beyond-exception-handling-condit... In the context of the example, I can easily imagine how the system might have processed most rows without issue, called out to a scoring system for most exceptional rows, and raised a few rows to human attention when even the scoring system couldn't figure it out.
- simion314 7y agoIs this lie instead of throwin errors/exceptions you raise Error events ?
- zeveb 7y agoYeah, something like that. One way to implement that is that error-event-raising is just a function which examines the dynamic context for error handlers, and calls them until one transfers control (e.g. by throwing an actual exception). That's pretty simple to implement in any language which has exceptions, panics or similar control-transfer structures. It gets more complex when you have restarts. The nice thing about Lisp is that all the complexity can be hidden with macros; with other languages you have to be explicit every time.
- tjpnz 7y agoI was asked to do something very similar at a film post production facility for credits. Essentially I was given a day and a half to reconcile spreadsheets of crew member names (compiled by department HODs) with their "credit name" on our employee database in order to create a master for the production company. I had all kinds of issues - variations of names, mispelled names, nicknames, names with the middle name used as a surname (and vice versa) and a few lacking even that. I recall leaning heavily on tables of common names, various Python string normalisation methods as well as soundex. In the end I was left with a dozen or so names which needed follow up but it was pretty good for the ~1000 I had started with initially. The most harrowing part of it all was attending the crew screening - it is actually possible to address issues on an end crawl (provided you catch it early enough) but I really didn't want to be the guy who screwed up the credit of someone who had just spent the previous three months in crunch to get the picture out the door.
- kstenerud 7y agoI can't shake the feeling that we're talking about different classes of problems here. Data importers and sanitizers are de-rigueur when dealing with real world data, of course. You have to expect to find crazy things inside, and be expected to just cope with it. That's not a problem. But when you're dealing with purely internal systems, the calculus is different. If you had to keep chasing down fopen(), printf(), and malloc() calls, you'd spend more time in administration than you would getting actual work done. So if I'm understanding this correctly, we're talking about purely internal code vs code that traverses domains (much like in DDD), which require different styles of solutions. Or am I still missing the point?
- Ovid 7y agoYou're not missing the point. I apparently didn't do a good job of making it clear. I'm primarily talking about external data/services. Anything which you would ordinarily consider "suspect", such as "reading from a third-party API", is the target here. The odds of printf() failing are so ridiculously low that I am not going to write tons of code to handle this case. It's not worth the money. The odds of putMoneyInCustomerAccount() failing are much higher, and the consequences are much graver, so that's a perfect candidate for saying "maybe just throwing an exception ain't the best solution here." In the case of OOP, particularly in Kay's vision, these objects might be written by someone else, using code we don't see, and thanks to isolation, might be connecting to third-party services which do all sorts of interesting things we don't know about or need to care about. Thus, they're not trusted. If I wrote the object or I can read its code, and I know what it's doing, I'm not going to sweat it. I'm also not arguing that we should always make the code that robust. Some things you can recover from or your budget might not allow you to make the system more robust.
- skybrian 7y agoI think some of the confusion here is that code you can't see or change is typically running on a different server, while ordinary method calls are typically about in-process communication. Compile-time checking makes sense for avoiding problems within code that's all going to be compiled together. Runtime error recovery makes sense for remote procedure calls. And there are also situations where you have untrusted code loaded dynamically (JavaScript in a browser). It seems pretty important to be clear about static assumptions you can trust (because it's compiled in) versus things that can change.
- jasode 7y agoThis blog post proposes a "data" sanity solution (alternative external oracles, or "scoring" on probabilities, etc) but that's not really what the comment by Samuel Falvo II was about. His problem was syntax getting broken and not about "questionable data". In the context of dynamic vs static compile, he's worried about scenarios like this: x = customer.last_name // worked on Friday and x=="Smith" - remove field customer.last_name // Saturday refactor + add field customer.full_name // Saturday refactor x = customer.last_name // broken on Monday with a runtime error With a static type system, the changes on Saturday would have told them immediately at compile time that the last_name field access by other client code was broken. (And yes, one typical answer for handling errors like that in dynamic type systems is unit tests -- but that's veering off into a different conversation.) This essay is a "solution" to a different problem.
- zeugmasyllepsis 7y ago> ...would have told them immediately at compile time that the last_name field access by other client code was broken. What's the proposed fix in this case? Should all clients accessing that field switch to `customer.full_name`? Do you control all of the call-sites? If so, that change is pretty easy to automate in dynamic languages by marking the field as deprecated, adding the new field, and forwarding `customer.last_name` to `customer.full_name`. That way, running systems don't break but you get the new behavior. You can automate logs to track when the deprecated field is accessed and, if necessary, fix call-sites after a reasonable period of monitoring, reducing risk (assuming there's not "once-in-a-century" paths in the code that invoke the call site). It seems reasonably likely that this is not the desired behavior - you may have external clients, and some callers may be relying on this field to actually return only a last name. In that case, deprecation is still the correct path, I think, since it's unreasonable to expect external clients to immediately fix systems relying on that field being available. Static typing can tell clients where they need to make changes, but it's not realistic demand that they immediately update every call site, every method depending on that behavior, and every integration with their external dependencies that relied on the interaction between your code and another system. This is why we have (semantic) versioning, deprecation warnings, and support agreements for larger systems. API's should evolve slowly and gracefully over time, not shift suddenly overnight.
- DonHopkins 7y agoI think there's a big difference between not understanding the message, and not understanding the data (a message parameter). When they get an unrecognized message, Objective C objects call their doesNotRecognizeSelector method, and Smalltalk objects call their doesNotUnderstand method. And the object sending the message can first check with respondsToSelector in Objective C or respondsTo in Smalltalk, before sending the message. But validating and sanitizing input parameters is a totally different thing than handling unknown messages, orthogonal to object oriented programming.
- jacobush 7y agoHm, I don't see how they are totally different and orthogonal. Objects and messages are kinds of types. Types can have constraints and conditions and there is all sorts of nuance. Difference, yes. But completely different? No... when designing a system there is a lot of freedom in where to draw the shapes of the system, how much information do we contain in the objects themselves, a hierarchy or inside parameters. "Stringy interfaces" is one extreme. There are many others.
- DonHopkins 7y agoI think you're barking up the wrong class hierarchy, trying to reformulate objects, messages and parameters as abstract data types. Alan Kay has been quite clear about his opinion that “Abstract Data Types” is not OOP. http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay... >One of the things I should have mentioned is that there were two main paths that were catalysed by Simula. The early one (just by accident) was the bio/net non-data-procedure route that I took. The other one, which came a little later as an object of study was abstract data types, and this got much more play. >If we look at the whole history, we see that the proto-OOP stuff started with ADT, had a little fork towards what I called "objects" -- that led to Smalltalk, etc.,-- but after the little fork, the CS establishment pretty much did ADT and wanted to stick with the data-procedure paradigm. [...] >(I'm not against types, but I don't know of any type systems that aren't a complete pain, so I still like dynamic typing.) >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. https://computinged.wordpress.com/2010/09/15/alan-kay-on-motis-objects-ever-cacm-article/ https://computinged.wordpress.com/2010/09/15/alan-kay-on-mot... >If you are “setting” values from the outside of an object, you are doing “simulated data structure programming” rather than object oriented programming. One of my original motivations for trying to invent OOP was to eliminate imperative assignment (at least as a global unprotected action). “Real OOP” is much more about “requests”, and the more the requests invoke goals the object knows how to accomplish, the better. “Abstract Data Types” is not OOP! https://news.ycombinator.com/item?id=10967103 https://news.ycombinator.com/item?id=10967103 >An interesting historical note is that the two inventors of Simula had completely different views of what they were doing and how it should be used for programming. Dahl was brilliant and conservative, and later wrote papers about using class definitions to make Abstract Data Types (and that is how a lot of so-called OOP programming is done today). Nygaard on the other hand was quite a wonderful wild man and visionary -- beyond brilliant -- and was into the abstract "simulate the meaningful structures" idea. Dahl was trying to fix the past and Nygaard was trying to invent the future. http://worrydream.com/EarlyHistoryOfSmalltalk/ http://worrydream.com/EarlyHistoryOfSmalltalk/ >"Object-oriented" Style >This is probably a good place to comment on the difference between what we thought of as OOP-style and the superficial encapsulation called "abstract data types" that was just starting to be investigated in academic circles. Our early "LISP-pair" definition is an example of an abstract data type because it preserves the "field access" and "field rebinding" that is the hallmark of a data structure. Considerable work in the 60s was concerned with generalizing such structures [DSP *]. The "official" computer science world started to regard Simula as a possible vehicle for defining abstract data types (even by one of its inventors [Dahl 1970]), and it formed much of the later backbone of ADA. This led to the ubiquitous stack data-type example in hundreds of papers. To put it mildly, we were quite amazed at this, since to us, what Simula had whispered was something much stronger than simply reimplementing a weak and ad hoc idea. 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 sites of higher level behaviors more appropriate for use as dynamic components. https://medium.com/@richardeng/goos-is-looking-at-it-from-the-perspective-of-abstract-data-types-56c6d98108d3 https://medium.com/@richardeng/goos-is-looking-at-it-from-th... >GOOS is looking at it from the perspective of Abstract Data Types. In Alan Kay’s conception of OOP, instead of static structures that are easy to reason, aliasing gives you dynamic systems of collaborating objects that are endlessly flexible and scalable, just like in nature’s biological systems of cells or the Internet of web servers. Proponents of ADT-style thinking, who use languages like C++ and Java, can’t imagine such complex systems, or they’re afraid of them. http://mythz.servicestack.net/blog/2013/02/27/the-deep-insights-of-alan-kay/ http://mythz.servicestack.net/blog/2013/02/27/the-deep-insig... >Where the big missing piece lacking in mainstream typed OO languages today is: >The big idea is “messaging” >He advocates focus should instead be on messaging and the loose-coupling and interactions of modules rather than their internal object composition: >The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviors should be. >And finds static type systems too crippling: >I’m not against types, but I don’t know of any type systems that aren’t a complete pain, so I still like dynamic typing. >Other popular languages embracing Smalltalk’s message-passing and late-binding and having implemented its message-based doesNotUnderstand construct include: forwardInvocation in Objective-C, method_missing in Ruby and more recently noSuchMethod in Google’s Dart. https://queue.acm.org/detail.cfm?id=1039523 https://queue.acm.org/detail.cfm?id=1039523 >Some people are completely religious about type systems and as a mathematician I love the idea of type systems, but nobody has ever come up with one that has enough scope. If you combine Simula and Lisp—Lisp didn’t have data structures, it had instances of objects—you would have a dynamic type system that would give you the range of expression you need. >It would allow you to think the kinds of thoughts you need to think without worrying about what type something is, because you have a much, much wider range of things. What you’re paying for is some of the checks that can be done at runtime, and, especially in the old days, you paid for it in some efficiencies. Now we get around the efficiency stuff the same way Barton did on the B5000: by just saying, “Screw it, we’re going to execute this important stuff as directly as we possibly can.” We’re not going to worry about whether we can compile it into a von Neumann computer or not, and we will make the microcode do whatever we need to get around these inefficiencies because a lot of the inefficiencies are just putting stuff on obsolete hardware architectures. https://news.ycombinator.com/item?id=19416424 https://news.ycombinator.com/item?id=19416424 >"I'm not against types, but I don't know of any type systems that aren't a complete pain, so I still like dynamic typing.)" >That's probably more controversial here than his views on OO.
- earthboundkid 7y agoThe article is making essentially the same argument as this one: https://dave.cheney.net/2019/01/27/eliminate-error-handling-by-eliminating-errors https://dave.cheney.net/2019/01/27/eliminate-error-handling-...
- geospeck 7y agoI've watched one of Rich Hickey's talk and at some point he brings that issue on the table, what happens when the receiver is not responding to the message. He is advocating Queues and one of his arguments is that Queues decoupling the requester from the receiver. So you don't really have to worry about things like this by using a queue. [1] https://youtu.be/ROor6_NGIWU?t=1955 https://youtu.be/ROor6_NGIWU?t=1955
- Fellshard 7y agoThat's abstracting in time more than in implementation; and it's primarily useful with stateful objects that are best reasoned about as a single, synchronous timeline (which most objects are). You'll see this pattern used in, say, Actor systems, as a queue-backed inbox.
- ngcc_hk 7y agoRepeat ... coupling and strict error processing kill token ring and let Ethernet win. So is sna and internet. Cross system require much flexibility and to allow for error.
- pron 7y ago> This is the software equivalent of "Not My Problem." ... This is how you deal with unanswered messages. You think about your response instead of just letting your software crash. A terrific 2014 paper, Simple Testing Can Prevent Most Critical Failures: An Analysis of Production Failures in Distributed Data-Intensive Systems [1] found that most catastrophic crashes in distributed systems are a result of catching exceptions and not paying thought to how to handle them. [1]: https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-yuan.pdf https://www.usenix.org/system/files/conference/osdi14/osdi14... (talk: https://www.usenix.org/conference/osdi14/technical-sessions/presentation/yuan https://www.usenix.org/conference/osdi14/technical-sessions/...)
- hi41 7y agoI have c programming experience but not java. I would greatly appreciate if you can tell me the where to catch an exception. When there are nested method calls I can’t tell where to handle it correctly - should I handle it in the top most function or the one where the current method was called from? Can you point me to some resources so that I can understand the correct way of handling exceptions.
- pron 7y agoAs the paper and the article say, what matters is not the technical aspect of where to handle an exception, but to give some serious thought to what should be done when it occurs (as opposed to just logging it). Serious problems happen when people don't pay enough attention to that.
- sundbry 7y agoYou should handle it in the most immediate context which can do something productive with it- if there's nothing you can do but swallow it, you should probably let it continue to pass up the stack. If you can handle it and log a warning and return something else, you should handle it there. For example, if you're writing an HTTP handler for an API, you should catch any exceptions at that point in your handler and return a 500 with an appropriate response to the client, since returning 500 is something productive we can do (vs letting it pass and crashing the server).
- pjc50 7y agoRight, so this is basically the Go approach: explicit error checking on everything. No exceptions, because exceptions are a weird sort of non-local control flow, and can escape and take down your program as a whole. The "if you don't know the answer, find an approximation from another route" is .. situational. In this case it's exactly what the customer wants. In other cases (finance, security, aerospace) it could be a disaster waiting to happen. I worked on a point-of-sale system where it was a matter of design philosophy that any inconsistency should be an immediate crash-and-reboot; since it also operated as a save-everywhere and audit-everywhere system, if you did manage to crash it then within 30 seconds you'd be back at the screen just before the last button press with no data loss other than the button press that caused the crash. I believe this crash-and-recover approach is very Erlang: https://ninenines.eu/articles/dont-let-it-crash/ https://ninenines.eu/articles/dont-let-it-crash/ Thinking of exceptions and message validation also makes me think of "in band" versus "out of band" signalling and errors. Exceptions are "out of band" - outside the normal function return process. Internet communication is all "in band" and the whole approach of separate control and data planes has almost entirely gone away, apart from SIP and (nearly dead) FTP.
- lostmyoldone 7y agoGo has panics, which is essentially exceptions with a different name. Failure to handle all panics will lead to process termination, as with unhandled exceptions.
- zeugmasyllepsis 7y agoTo be fair, their general use is discouraged unless you want the process to terminate. Not that you can't do that in other languages, but the culture in Go seems to prefer returning error codes.
- theoh 7y agoSum types arguably permit a form of out-of-band return value. A function that returns a sum type has the choice of returning the usual type (an integer value, say) or an error value of some other type. IMO this counts as a kind of out-of-band arrangement because it doesn't involve using one of the normal return values to signal error. https://en.wikipedia.org/wiki/Semipredicate_problem https://en.wikipedia.org/wiki/Semipredicate_problem
- frou_dh 7y agoThere's something I don't understand about the "messaging" metaphor as opposed to the more pedestrian method calling system that most people are used to... What about private methods? Decently written objects in the wild tend to have some private methods that they call on themselves. The highfalutin metaphor of a message goes out the window there, because it's an individual object doing something to itself, not a communication.
- corysama 7y agoThat's true if there is no composition going on within the object. But, once you have a class hierarchy, some mixins, some dependency injection, or some such... you are "privately" talking to parts of yourself that come from elsewhere.
- frou_dh 7y agoBut when it's the exact same syntax to call a statically-dispatched method on the current object as it is to send a message to something else, it breaks the illusion of messaging being meaningfully different while writing code. Whether some actual bonafide communication ends up being done one level removed (e.g. inside such a private method, or chained off of a field access) is immaterial because when appraising the conceptual integrity we cannot continue after seeing a fail. (edited)
- dnautics 7y agoam I crazy about this? You should have (at least two) type of message guarantees - "don't care" and "must respond", kind of like "udp" and "tcp". Obviously other messaging systems have other types of guarantees, but these seem like they are a reasonable basic. If your agent that send a "must respond" message doesn't get a response within some customizable time, then it itself fails loudly.
- mcguire 7y agoNote that "...how you might handle objects that don't respond to messages" refers specifically to what happens when you send a message (or call a method, in the parlance of most non-Smalltalk languages) thingy.spamford() and the thingy doesn't implement a handler (or a method) for spamford. It has to do with this paragraph from the original, original post: "Kay argues (correctly, IMHO), that the computer revolution hasn't happened yet because while our bodies are massively scalable meat computers, our silicon computers generally don't scale in the slightest. This isn't just because silicon is slow; it's because of things like print customers.fetch(customer_id).last_name not actually having a last_name method, throwing an exception (assuming it compiled in the first place) and programmers getting frantic late-night calls to bring the batch system back up. The only real upside is that it offers job security." This has nothing to do with network protocols.