Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This "be liberal in what you accept" idea, applied to modern programming, always struck me as strange.

Yes, taking an unknown structure in your program is the easy part. Programming against an unknown structure is where the problem lies.

I'd love to hear more examples of programming against such input that are beneficial over "parse don't validate" idea.



It's called Postel's law, and it's one of the burdensome idiocies Unix programmers have saddled us with, along with text-file formats and protocols, null-terminated strings, fork(2), and the assumption that I/O is synchronous by default.

Of course, once you adopt a "follow the spec or GTFO" stance, you reap other benefits as well; for example you are free to adopt a sensible binary format :)


The problem right there is in the definition of “liberal”.

A cautious, forward-thinking designer-developer would interpret it as “allow for unknowns”. Whereas sloppy-ass cowboys think it means “accept any old invalid crap” and pass their garbage accordingly.

One of these philosophies gave us HTTP; the other HTML. And no prizes for knowing which one is an utter horror to consume (an arrangement, incidentally, that works swimmingly well for its entrenched vendors and absolutely no-one else).


Posted was not a Unix programmer when he formulated his law


In Clojure you generally accept any kind of map, do some operation and return 'copy' of that map, or pass it on.

A simple example is a ring web stack, where request flow threw different functions that transform the http request.

Each function just assumes the keys its needed are there, or to do input validation, you just validate that the incoming map has the keys that you require but does not care what else is in there. Clojure Spec will also do validation of the value in that key, even if it is an arbitrary complex map.

What this gets you is that you can pass around generic structures, that can be used with all standard Clojure functions and most libraries, validation is applied as needed on those keys that you need.


But I believe this is also with the spirit of the article. Your functions still assume something about input and are polymorphic over everything else in it.

AFAIK this can also be done in a type safe manner in languages which support extensible records/row polymorphism.


You don't need row polymorphism to write a function which operates on a collection of key/value pairs and expects a specific key to exist at runtime.

This works in Haskell, just as it does in Clojure.


Please write the Haskell type signature of such a function, as an example.


    f :: Map String String -> Map String String


There is no reason why the same would not be possible in a statically-typed language.


> This "be liberal in what you accept" idea, applied to modern programming, always struck me as strange.

Agreed. There's a reason that serious correctness-oriented languages, like Ada, do not use this approach.


Almost nobody uses Ada, though, while one of the most read programming language-related websites (lambda-the-ultimate) is written in a dynamic language. Also, in the day-to-day interactions of the real world we almost never carry out “validation”, we stop at “parsing” most of the time, otherwise almost nothing will ever be done.

In other words “walks like a duck/quacks like a duck” is enough for most of the open world operations, no need for Platonic-like ideals or Aristotle-like categorizations. In the real world we can still use any piece of wood or stone as a table as long as food doesn’t fall off it, because said piece of wood/stone quacks and walks like a table, while Plato or Aristotle or most of the static type proponents would argue that we shouldn’t do that as that piece of stone or wood doesn’t have 4 legs and as such is not really a table/doesn’t correspond to the idea/type of a real table.


> In the real world we can still use any piece of wood or stone as a table as long as food doesn’t fall off it, because said piece of wood/stone quacks and walks like a table, while Plato or Aristotle or most of the static type proponents would argue that we shouldn’t do that as that piece of stone or wood doesn’t have 4 legs and as such is not really a table/doesn’t correspond to the idea/type of a real table.

You’re advocating for structural typing, which is a property of (many) static type systems. The view you project onto Aristotle/Plato/static-type-advocates is nominal typing, which is just a different kind of static typing. Many languages support both kinds (for example, Go, Rust, and Python’s static type system, Mypy).

Note that languages with structural typing will correctly accept your stone table and reject objects unsuitable for holding food (where “holding food” is the structure a type must implement) while a dynamic type system will happily allow anything to be passed in as the table, including a waterfall or communism and you’ll be none the wiser until your bread is wet or your country is starving.


> (where “holding food” is the structure a type must implement) while

I guess it's turtles all the way down, because (from my pov at least) deciding on what "structures a type must implement" is pretty similar to saying "a table is a table only if it's got 4 legs". Again, from my pov, a dynamic-like language is a lot more tolerant with the unknown unknowns of which the open world is full of, it doesn't depend on any "structure implementation" being defined or on anyone else counting the legs of said table.


> I guess it's turtles all the way down, because (from my pov at least) deciding on what "structures a type must implement" is pretty similar to saying "a table is a table only if it's got 4 legs".

The idea is that callers/clients define the structure they’re going to use. If the caller needs something that can hold food, then only things that hold food can be passed in. If the caller depends on having four legs, then only things with four legs can be passed in. If the caller depends on four legs and can hold food, only objects that satisfy both properties can be passed in. The type doesn’t choose which structures it implements (that’s nominal typing) the type just is and it either implements a contract / has the right structure for a given client or it doesn’t. Check out Go’s interfaces for a more concrete example.

> Again, from my pov, a dynamic-like language is a lot more tolerant with the unknown unknowns of which the open world is full of, it doesn't depend on any "structure implementation" being defined or on anyone else counting the legs of said table.

This is inherently untrue because structural typing is simply the formalization of the same logic you use when composing programs in a dynamic language. The same logic that lets Python document an argument as “file-like” is formally expressed in a static language as “anything with methods read(), write(), seek(), close(), etc”. This still gives you the openness you need (you can pass in other types even if they don’t know about your file-like interface) but it guards against runtime TypeErrors and AttributeErrors.

Give static typing an earnest try. And be discerning about the source of friction you encounter: is it because static typing is making it harder for you to write big-prone code? Is it because the language makes heavy use of inheritance (and nothing to do with static typing at all)? Is it because the language is very verbose (and nothing to do with static typing at all)? Or maybe the language has a bizarre syntax and jargon-laden, conflicting documentation, in which case these also aren’t properties of type systems but rather functional languages ;).


In fairness, there's also a reason why correctness-oriented languages don't see a lot of use in the industry.


Because most programmers are sloppy.

When you can't afford to be sloppy -- when human lives are literally on the line -- correctness-oriented programming comes roaring back into vogue.


Most Clojure teams I speak to spec their input using Clojure spec, then they get custom recursive error reporting, validation, coercion and generators

Clojure specifications can also be converted into other forms of specification like graphql schema, database schema, json schema etc

The best I've seen a type system do on user input is blow up at runtime and that's not good enough for me


> The best I've seen a type system do on user input is blow up at runtime and that's not good enough for me

Then you haven’t been looking closely enough. The article addresses exactly this wrong argument.


Does it?

What happens to the example code if UserName is an Int?


The article demonstrated that with Haskell — as with just about any other language — you can choose to silently ignore malformed input. There is absolutely no fundamental necessity to "blow up at runtime" as the parent comment erroneously posited.


Who’s applying Postel’s Law to programming? Citation please.

It was intended for network protocols/distributed systems communications — situations with extremely loose coupling between components. It’s useful in this very specific paradigm, not for arbitrarily programming anything.




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

Search: