10 ms·
Can't we extend this argument to eliminating basically all static typing? And frankly that'd not even be wrong, and is why Alan Kay defined OOP as one that's dy
by 3cats-in-a-coat 3y ago
Can't we extend this argument to eliminating basically all static typing? And frankly that'd not even be wrong, and is why Alan Kay defined OOP as one that's dynamically typed and late bound, and we went against it anyway to keep relearning the same lessons over and over.
- kentonv 3y agoThe argument is really more like: Always defer validation until the point where the data is actually consumed, because only the consumer actually knows what is valid. Which is definitely a counterpoint to the oft-stated argument that you should validate all data upfront. Either way though, you can still have types, the question is just when and where (in a distributed system, especially) they should be checked.
- lanstin 3y agoThe distributed part shifts the problem from "find types that represent your solution" to "find a system of types that enable evolution of your solution over time." I think this is why bad things like json or xml do so well: they work fine with a client dev saying, "I need this extra data" and the server dev adding it, and then the client dev consuming it. The more modern approaches, like protobuf or capn proto are designed with the experience of mutating protocols over time. It works pretty well too unless the new field changes the semantics of old field values, e.g. adding a field "payment_is_reversal_if_set" to a payment info type, which would change the meaning of the signs of the amounts. In that case, you have to reason more explicitly about when to roll out the protocol readers and when to roll out the protocol writers. Or version it, etc.
- mike_hearn 3y agoThe argument is actually more like: don't use badly written middleman software that tries to parse messages it doesn't need to parse. I was at Google when the "let's get rid of optional" crusade started. It didn't make sense to me then and over a decade later it still doesn't. If a program expects a field to be there then it has to be there, removing the protobuf level checking just meant that programs could now read garbage (some default value) instead of immediately crashing. But the whole reason we have types, assertions, bounds checking and so on is because, almost always, we'd like our software to NOT just blindly plough on into undefined territory when it doesn't understand something properly, so in reality it just means everyone ends up coding those very same required-ness assertions by hand. Now, Google's business model is remarkably robust to generating and processing corrupt data, so you can argue that in the specific case of this specific company, it is actually better to silently serve garbage than to crash. This argument was made explicitly in other forms, like when they deleted all the assertions from the HTTP load balancers. But in every case where I examined an anti-required argument carefully the actual problem would turn out to be elsewhere, and removing assertions was just covering things up. The fact that so much of Google's code is written in C++ that not only starts up slowly but also just immediately aborts the entire process when something goes wrong also contributes to the brittleness that encourages this kind of thing. If Google had been built on a language with usable exceptions right from the start it'd have been easier to limit the blast radius of data structure versioning errors to only the requests where that data structure turned up, instead of causing them to nuke the entire server (and then the RPC stack will helpfully retry because it doesn't know why the server died, promptly killing all of them). But this tolerance to undefined behavior is not true for almost any other business (except maybe video games?). In those businesses it's better to be stopped than wrong. If you don't then you can lose money, lose data, lose customers or in the worst cases even lose your life. I don't think people appreciate the extent to which the unique oddities of Google's business model and infrastructure choices have leaked out into the libraries their staffers/ex-staffers release.
- kentonv 3y ago> The argument is actually more like: don't use badly written middleman software that tries to parse messages it doesn't need to parse. The middleman software in question often needed to process some part of the message but not others. It wasn't realistic to define a boundary between what each middleman might need and what they wouldn't need, and somehow push the "not needed" part into nested encoded blobs. I'm not sure the rest of your comment is really addressing the issue here. The argument doesn't have anything to do with proceeding forward in the face of corrupt data or undefined behavior. The argument is that validation needs to happen at the consumer. There should still be validation.
- 3cats-in-a-coat 3y agoHonestly I wonder what is the big win in terms of performance by using static types here, because this sounds so terribly well fit for dynamic types (of which optionality by default is in fact a limited example). Such an odd choice to calcify a spec in a places where it changes all the time. "Static" optimizations should be local, not distributed.
- mike_hearn 3y agoI think you're defining consumer as the literal line of code where the field is read, whereas a more natural definition would be something like "the moment the data structure is deserialized". After all it's usually better to abort early than half way through an operation. It was quite realistic to improve protobufs to help dig web search out of their "everything+dog consumes an enormous monolithic datastructure" problem, assuming that's what you're thinking of (my memory of the details of this time is getting fuzzy). A simple brute-force fix for their situation would have been to make validation of required fields toggle-able on a per-parse level, so they could disable validation for their own parts of the stack without taking it away for everyone else (none of the projects I worked on had problems with required fields that I can recall). A better fix would have been for protobufs to support composition. They could then have started breaking down the mega-struct into overlapping protos, with the original being defined as a recursive merge of them. That'd have let them start narrowing down semantically meaningful views over what the programs really needed. The worst fix was to remove validation features from the language, thus forcing everyone to manually re-add them without the help of the compiler. Really, the protobuf type system was too simple for Google even in 2006. I recall during training wondering why it didn't have a URL type given that this was a web-centric company. Shortly after I discovered a very simple and obvious bug in web search in which some local business results were 404s even though the URL existed. It had been there for months, maybe years, and I found it by reading the user support forums (nobody else did this, my manager considered me way out of my lane for doing so). The bug was that nothing anywhere in the pipeline checked that the website address entered by the business owner started with https:// https://, so when the result was stuffed into an <a> tag it turned into <a href="www.business.com"> and so the user ended up at https://www.google.com/www.business.com https://www.google.com/www.business.com. Oops. These bad strings made it all the way from the business owner, through the LBC frontend, the data pipeline, the intermediate microservices and the web search frontends to the user's browser. The URL did pass crawl validation because when loaded into a URL type, the missing protocol was being added. SREs were trained to do post-mortems, so after it got fixed and the database was patched up, I naively asked whether there was a systematic fix for this, like maybe adding a URL type to protobufs so data would be validated right at the start. The answer was "it sounds like you're asking how to not write bugs" and nothing was done, sigh. It's entirely possible that similar bugs reoccurred dozens of times without being detected. Those are just a couple of cases where the simplicity (or primitivity) of the protobuf type system led to avoidable problems. Sure, there are complexity limits too, but the actual languages Googlers were using all had more sophisticated type systems than protobuf and bugs at the edges weren't uncommon.
- insanitybit 3y ago> Can't we extend this argument to eliminating basically all static typing? No, because static typing exists in all sorts of places. This argument is primarily about cases where you're exchanging data, which is a very specific use case.
- 3cats-in-a-coat 3y agoStatic types are a partial application/reduction when certain mutable or unknown variables become constants (i.e. "I for sure only need integers between 0-255 here"). I'm not rejecting static types entirely, and yes I was discussing exchanging data here, as Alan Kay's OOP is inherently distributed. It's much closer to Erlang than it is to Java.
- insanitybit 3y ago> I'm not rejecting static types entirely, and yes I was discussing exchanging data here OK I guess I'm having a hard time reconciling that with: > basically all static typing
- 3cats-in-a-coat 3y agoSorry, I see how I'm vague. The idea is you have no "pre-burned" static types, but dynamic types. And static types then become a disposable optimization compiled out of more dynamic code, in the same way JIT works in V8 and JVM for example (where type specialization is in fact part of the optimization strategy).
- insanitybit 3y agoYou're describing dynamic types
- 3cats-in-a-coat 3y agoBut with the benefit of static types, and without the drawbacks of static types.
- kccqzy 3y agoDo you need to make different versions of a program exchange information even though they do not agree on the types? No? Then this argument cannot be extended this way.
- mrkeen 3y agoIt's up to you. It's easy to imagine any statically typed language having a general-purpose JSON type. You could imagine all functions accepting and returning such objects. Now it's your turn to implement the sum(a,b) function. Would you like to allow the caller to pass anything in as a and b?
- lanstin 3y agoThis is like when people use protobuf to send a list of key-value mappings, and call that a protocol. (I've seen that same design in many protocol description arenas, even SQL database schemas that are just (entityId INT, key CLOB, value BLOB).
- chubot 3y agoSee my sibling comment, e.g. with respect to Rich Hickey's framing - https://news.ycombinator.com/item?id=36911033 https://news.ycombinator.com/item?id=36911033