←back to thread

Parse, Don't Validate (2019)

(lexi-lambda.github.io)
389 points melse | 4 comments | | HN request time: 3.48s | source
Show context
seanwilson ◴[] No.27640953[source]
From the Twitter link:

> IME, people in dynamic languages almost never program this way, though—they prefer to use validation and some form of shotgun parsing. My guess as to why? Writing that kind of code in dynamically-typed languages is often a lot more boilerplate than it is in statically-typed ones!

I feel that once you've got experience working in (usually functional) programming languages with strong static type checking, flakey dynamic code that relies on runtime checks and just being careful to avoid runtime errors makes your skin crawl, and you'll intuitively gravitate towards designs that takes advantage of strong static type checks.

When all you know is dynamic languages, the design guidance you get from strong static type checking is lost so there's more bad design paths you can go down. Patching up flakey code with ad-hoc runtime checks and debugging runtime errors becomes the norm because you just don't know any better and the type system isn't going to teach you.

More general advice would be "prefer strong static type checking over runtime checks" as it makes a lot of design and robustness problems go away.

Even if you can't use e.g. Haskell or OCaml in your daily work, a few weeks or just of few days of trying to learn them will open your eyes and make you a better coder elsewhere. Map/filter/reduce, immutable data structures, non-nullable types etc. have been in other languages for over 30 years before these ideas became more mainstream best practices for example (I'm still waiting for pattern matching + algebraic data types).

It's weird how long it's taking for people to rediscover why strong static types were a good idea.

replies(10): >>27641187 #>>27641516 #>>27641651 #>>27641837 #>>27641858 #>>27641960 #>>27642032 #>>27643060 #>>27644651 #>>27657615 #
ukj ◴[] No.27641651[source]
Every programming paradigm is a good idea if the respective trade-offs are acceptable to you.

For example, one good reason why strong static types are a bad idea... they prevent you from implementing dynamic dispatch.

Routers. You can't have routers.

replies(3): >>27641741 #>>27642043 #>>27642764 #
kortex ◴[] No.27641741[source]
Sure you can. You just need the right amount of indirection and abstraction. I think almost every language has some escape hatch which lets you implement dynamic dispatch.
replies(1): >>27641809 #
ukj ◴[] No.27641809[source]
This is a trivial and obvious implication of Turing completeness. Why do you even bother making the point?

With the right amount of indirection/abstraction you can implement everything in Assembly.

But you don't. Because you like all the heavy lifting the language does for you.

First Class citizens is what we are actually interested in when we talk about programming language paradigm-choices.

https://en.wikipedia.org/wiki/First-class_citizen

replies(3): >>27641950 #>>27641980 #>>27641983 #
dkersten ◴[] No.27641980[source]
> Why do you even bother making the point?

Maybe because you said:

> they prevent you from implementing dynamic dispatch.

and

> Routers. You can't have routers.

Which just isn't true. You can implement dynamic dispatch and you can have routers, but they come at a cost (either of complex code or of giving up compile-time type safety, but in a dynamic language you don't have the latter anyway, so with a static language you can at least choose when you're willing to pay the price).

> First Class citizens is what we are actually interested in when we talk about programming language paradigm-choices.

But that's not what you said in your other comment. You just said you can't have these things, not they're not first class citizens. Besides, some static languages do have first class support for more dynamic features. C++ has tools like std::variant and std::any in its standard library for times you want some more dynamism and are willing to pay the tradeoffs. In Java you have Object. In other static languages, you have other built-in tools.

replies(2): >>27642019 #>>27642836 #
ukj ◴[] No.27642019[source]
Everything comes at a cost of something in computation!

That is what “trade offs” means.

You can have any feature in any language once you undermine the default constraints of your language. You can implement Scala in Brainfuck. Turing completeness guarantees it!

But this is not the sort of discourse we care about in practice.

https://en.wikipedia.org/wiki/Brainfuck

replies(1): >>27642114 #
dkersten ◴[] No.27642114[source]
Yes, and? How is that relevant here?

You said "you can't", kortex said "you can" and then you moved the goal posts to "you can because of turing completeness, but its bad, Why do you even bother making the point?" to which I replied "because its a valid response to you're `you can't`" now you moved them again to "everything comes at a cost" (which... I also said?).

Of course everything comes at a cost and yes, that's what "trade off" means. Dynamic languages come at a cost too (type checking is deferred to run time). So, this time, let me ask you: Why do you even bother making the point?

replies(2): >>27642153 #>>27642305 #
ukj ◴[] No.27642153[source]
Tractability vs possibility.

You don't grok the difference.

You can implement EVERYTHING in Brainfuck. Tractability is the reason you don't.

The goalposts are exactly where I set them. With my first comment.

"Every programming paradigm is a good idea if the respective trade-offs are acceptable to you."

replies(2): >>27642301 #>>27643931 #
1. dkersten ◴[] No.27642301[source]
Its perfectly tractable though. Just because you don't understand it or don't think it is, doesn't make it true.

> "Every programming paradigm is a good idea if the respective trade-offs are acceptable to you."

That's not what we are responding to. Nobody here is arguing over this statement. We are responding to you assertion that static typed compile-time checked languages _prevent_ you from having dynamic dispatch and that you _can't have_ routers because of that. Neither of which are true.

Dynamic languages prevent you from having compile time checks. Does that make them bad? Static languages give you compile time safety, but if you're willing to forego that [1], then you can get the EXACT SAME behavior as dynamic languages give you.

You literally said:

    For example, one good reason why strong static types are a bad idea... they prevent you from implementing dynamic dispatch.

    Routers. You can't have routers.
Nowhere did you say anything about trying to implement it at compile time. Also, if strong static types are a bad idea because you can't maintain them all the time, then dynamic typed languages are a bad idea because you don't get static types ever, its always at runtime.

Just because a hammer can't screw in screws doesn't mean its a bad idea, it just means that you can't use it for all use cases. This is the same. You can use static types and for the few cases where you need runtime dynamism, then you use that. That doesn't make the static types in the rest of your code bad. It just gives you additional tools that dynamic types alone don't have.

[1] to various degrees, its not all or nothing like you seem to be implying, there are levels of middle ground, like std::variant which maintains safety but you need to enumerate all possible types, or std::any which is fully dynamic but you give up compile time checks

replies(2): >>27642314 #>>27642370 #
2. ◴[] No.27642370[source]
3. dkersten ◴[] No.27642533[source]
Ok, I think I understand what you are trying to say. Instead of telling us how we don’t understand various things, how about next time you define your terms, choose your words more carefully and be clearer with your explanations, so we can actually understand what you’re trying to say.

So let me restate what I think you meant. Maybe I’m wrong.

> a limitation of compile time static types is that they cannot do dynamic dispatch, which is relies on information that isn’t known at compile time.

I think we can all agree on this?

Note that I said it’s a limitation, not that it’s bad. Limitations don’t make something bad in themselves, they only constrain when it is an appropriate tool. You wouldn’t say that a submarine is bad because it can’t go on land? It’s a limitation but that doesn’t make it bad.

Then you said that because of this limitation, “you can’t have routers”.

But you can have routers in static languages like C++, Rust or even Haskell.

You have a choice (trade off) to make:

1. either you give up on some dynamism to keep compile time checks by enumerating the types you can dispatch on (as std::variant does)

2. or you choose to give up static checking (for this part of your code only!) and move it to runtime first full dynamic logic (as std::any does). When giving up compile time safety for one part of your code, you do not give it up for all of your code, because you can runtime validate then on the boundaries going back into statically checked code paths, so the compiler cab assume that they are valid if they get back in (as the runtime checks would reject bad types)

Neither case is “you can’t have routers”, but sure you can’t have fully dynamic routers purely at compile time.

Also, both options are perfectly tractable and both cases are typically first class language features (at least in the static types languages I’ve used). In no case are the “bad” options, despite each option having different limitations.

In a dynamic-types-only language, you don’t get to choose the trade offs at all, you only get “fully dynamic no compile time checking, runtime checks only”.

Note also that in real life, few things are truly fully dynamic. You are always constrained by the operations that are expected to be carried out on the dynamic data and while you might not know at runtime what days you could get, you DO know what operations you expect to run on the data. And you can perform compile time checks around that.

So for a router, so you really need it to be fully dynamic? Or can you live with it being generic (ie it’s a library that does routing and it can support any type, but you constrain it to known types when you actually use it). If so, you have options that maintain type safety: you can use OOP inheritance, you can use enumerated types like std::variant, you can use generics/templates. The library works for any types but the user chooses what types to implement based on the operations that will be performed on the data. Even dynamic types do this, they just defer it to runtime.

Or you can have the router operate on purely dynamic types but the handlers that are routed to are statically typed (eg if in C++ the router uses std::any and the handler registers what types it can accept and the router checks validity before handing the data off).

replies(1): >>27642610 #
4. dkersten ◴[] No.27642752{4}[source]
I don’t think any of the replies you got for your first comments were uncharitable at all, they responded to what you wrote.

At that point, you could have corrected us and revised your wording for clarity, but you did not, you dug your heels in, you moved the goal posts, you claimed we don’t understand various things that weren’t even really related to the comment we were responding to and you brought in irrelevant points like Turing completeness. You didn’t “get lynched“ right away, you could have reworded or clarified or asked what we didn’t understand about your statement.

Also, YOU didn't practice the principle of charity!

When people responded to what you wrote, you dug in and claimed we didn't understand compilers or tractability vs possibility and various other things, rather than thinking "maybe they didn't understand my point, I should clarify". So its on you, not us.

I’m still not sure if I understand what you were trying to say, I am assuming that you meant what I wrote when I restated your comment, by piecing all of your different comments together. I’m still not sure if you actually meant static types are bad period (vs being bad at certain things and having certain limitations). And I still don’t agree with “you can’t have routers”.

Anyway, I’m done, have a nice day.

EDIT: I know I said I'm done, but your reply: "parse better", I gave you an out and still you blame everyone else and don't accept that you might have made a mistake. You're so sure that you are right and everyone else is wrong that you don't even entertain the possibility that you might have made a mistake (either in your reasoning or your explanation thereof). You appear to have an ego problem. You should take some time out and reflect on your life a bit.

replies(1): >>27642765 #