Live data from Hacker News

Parse, Don't Validate (2019)

lexi-lambda.github.io

251–260 of 288 posts

Re: Parse, Don't Validate (2019)

#251

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 ty…

For what its worth: People don't use dynamic language because they don't know better or never used a static language. To better understand what dynamic languages bring to the table, here are some disadvantages of static types to consider: Static types are awesome for local reasoning, but they are not that helpful in the context of the larger system (this already starts at the database, see idempotency mismatch). Code…

In my own anecdotal experience, I think it comes down to what the product you are working on needs, and how your team works.

Recently, I spent 3 years on Scala then switched jobs and spent 3 years in Ruby.

It was a shock to go back to dynamic languages, but after 3 months, I honestly couldn't tell which felt more productive or led to more stable high quality product.

In Ruby, we had all the issues people point out about dynamic languages, but the product didn't lean heavily on complex data structures or algorithms. We embraced complexity and failure and get good processes, designs and practices to deal with this.

In Scala, we had more rigour, but I also know I spent a lot of time on type design. Once things were sorted there was a lot of confidence in it, but generally, it took a lot longer to get there.

For certain systems that is absolutely worth it, for others (and in my case) it did feel like the evolution of the product meant this effort never really paid off.

Re: Parse, Don't Validate (2019)

#252
post #187

Earlier quoted context omitted.

So, there is nothing dynamic about that dispatch. Because the implementation details of Foo are actually know at compile time. Which is why you are able to type-check it. You have literally declared all allowed (but not all possible) implementations of Foo. What happens when Foo() is a remote/network call?

That is not what dynamic dispatch means! It is an extremely well established term, with a very clear meaning, and that is not what it means. I thought you were just mistaken about something, but no, instead you've redefined a well understood term without telling anyone, then aggressively refused to clarify what you meant by it and argued for hours with people, while saying they were all wrong when they used the well…

It turns out "dynamic dispatch" is not as well established or as clear as you insist. It means at least one of five things:

0. Dynamic dispatch (as you are using it) 1. Double dispatch 2. Multiple dispatch 3. Predicate dispatch 4. All of the above collectively.

What you thought was a confusing re-definition on my part might have been an ignorance of alternative uses on yours ;)

Java doesn't do 1, 2 or 3.

Re: Parse, Don't Validate (2019)

#253
post #171

Earlier quoted context omitted.

No this was like over a week, and 100% due to the tooling. Pydantic, pycharm, black, mypy, and flake8. Pretty much went from "type hints here and there" to "what happens if I try writing python as if it were (95%) statically typed." I'd been testing well before this point but it's not the same as writing test. The development process is totally different when you write structured types first and then write your logic…

The development process is totally different when you write structured types first and then write your logic. 10/10 would recommend. Unless you were writing very small throwaway scripts, in what world where you writing your logic first and thinking about your data structures later?

Even having type hints on basic functions like def foo(bar: str) in a throwaway-script helps because it gives me reliable autocompletion on bar. I might be getting old or toggle between so many different languages nowadays that even basic stuff i don't want to remember, like whether it is bar.lower(), bar.lowerCase(), bar.toLower(), bar.toLocaleLowerCase().

Defining a data structure up front doesn't require a lot of boilerplate as Java incorrectly have taught all of us. Writing a statically typed typing.NamedTuple or @dataclass is literally a one-liner.

Re: Parse, Don't Validate (2019)

#254

Earlier quoted context omitted.

Plenty of people know multiple statically typed and dynamic languages, and multiple functional, imperative, and other languages; and use dynamic languages for some things but not other things. The set of people using dynamic languages isn't just "those that haven't had their eyes opened yet to what static languages can do". Different languages and paradigms make different things easier. I do believe that, for long la…

For sure. I do most of my work in situations of high volatility of domain and requirements and relatively high risk. (E.g., startups, projects in new areas.) Static typing really appeals to me on a personal level. I enjoy the process of analysis it requires. I love the notion of eliminating whole classes of bugs. It feels way more tidy. I took Odersky's Scala class for fun and loved it. But in practice, they're just…

> But in practice, they're just a bad match for projects where the defining characteristic is unstable ground. They force artificial clarity when the reality is murky.

The rebuttal to this is provided by another post from Alexis King: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ...

> This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying the world” or pinning down the structure of every value in a system. The reality is that static type systems allow specifying exactly how much a component needs to know about the structure of its inputs, and conversely, how much it doesn’t.

Static typing helps you to safely model the parts of the system that you actually do know about, while allowing you to leave the unknown parts loosely defined. And when business requirements change, the compiler helps you to make those changes in a safe, guided way. This is why people often report the experience of doing large refactorings driven by compiler error messages, then running and finding that the change works correctly on the first try. A confidence few report in dynamically-typed languages.

> After doing a couple dozen, I'm seeing a pattern: developers using static tooling (e.g., Java, TypeScript) get circa half as much done as people using dynamic tooling (Python, plain JS).

I hope you understand and accept the fact that interview code challenges are not very representative of real-world software engineering. On-the-spot interview code is throwaway; real production code is not.

Re: Parse, Don't Validate (2019)

#255

Earlier quoted context omitted.

The example with pattern matching doesn't have anything to do with static types. You'll have exactly the same problem if you pattern match against positional arguments in Python: match event.get(): case Click((x, y)): handle_click_at(x, y) (Example from PEP 636[1].) In both Python and statically typed languages you can avoid this by matching against field names rather than positions, or using some other interface to…

You're absolutely right. I guess I mentioned pattern matching in particular because of the cited sentence from OP "I'm still waiting for pattern matching + algebraic data types". > The same is true for the rest of the things you've mentioned: none are specific to static typing! Sure, I could be wrong here. I frequently am. But could you point out why do you think that?

Seems like static typing is neither necessary nor sufficient to cause the particular problems you mentioned.

Static types can absolutely help with more than local reasoning. One of the main reasons I like static types is that they give me a place in the code to reflect aspects of the architecture that otherwise go implicit. Databases are a great example: in both static and dynamic languages, the behavior of your code is going to depend on the database schemas you're using, and static types give you a mechanism to declare and manage this dependency explicitly. In a general sense, the key in architecture is how a system is split up into parts and what interfaces the parts have, and a static type system is a way to make those interfaces first-class citizens of a language subject to abstraction and automation.

Code with static types might be larger than the problem it's trying to solve, but so might code with dynamic types—that's more a function of the sort of abstractions and complexity that go into the code. I've written some code to do very similar things in Haskell and Python, and my experience is that the Haskell code tends to be noticeably more compact than the Python code, even though I make liberal use of types and write out all the top-level type signatures. While some of this comes down to other differences between the languages, part of Haskell's expressiveness absolutely comes down to static types—static types make it easier to write very general code against high-level abstractions (eg monads) that would need to be narrower in dynamic languages (eg promises).

And sure, you can couple code and data in static languages, but at least the types will help you when it comes time to change. I've worked with dynamically typed programs where functions rely on some mixture of nested tuples, hashmaps, lists... etc, and it's hard to understand exactly how concepts are represented in code, much less change that representation. If you represent some central notion in your code as some nested primitive type (a hashmap of tuples of ...) and you want to change that representation, you'll still have to update the places it's used in the code, but without a static type system you won't get a list of those places, and you won't have assurance that updated everywhere that mattered. I'm not sure I'm explaining this pattern well, but I've worked in JavaScript codebases where making breaking changes to core data components was basically impossible.

Point being, all the problems you mention come up in both static an dynamic languages. There might be a case that they're more common in one or the other, but it's not obvious either way, and it's going to depend a lot on which specific static type system you're thinking of.

Re: Parse, Don't Validate (2019)

#256
post #28

This principle can be applied to dynamic languages as well if you have some mechanism such as type hinting, pre-conditions etc. that are checked by a linter during development, even if it isn't, you can still use it at runtime with sufficient error handling. The essential point of this blog post is to avoid "shotgun parsing", where parsing/validating is done just from a procedural standpoint, where it matters when ex…

I think the article goes a little further than what you describe -- it would have you use a strong type that cannot represent illegal values. There's a follow-up article by the same author (that I unfortunately can't find), in which she explains this point. As an example, returning a NonZero newtype over Int is not as type safe as using an ADT that lacks a zero value altogether. Using a NonEmpty newtype over List is…

> There's a follow-up article by the same author (that I unfortunately can't find), in which she explains this point.

I think this is it: https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-...

Re: Parse, Don't Validate (2019)

#257
post #66

Earlier quoted context omitted.

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.

Are you sure you know what dynamic dispatch is? Java has dynamic dispatch, and it is a statically typed language. In Java, it's often called "runtime polymorphism". https://www.geeksforgeeks.org/dynamic-method-dispatch-runtim... And using it doesn't give up any of Java's type safety guarantees. The arguments and return type of the method you call (which will be invoked with dynamic dispatch) are type checked.

In English there seems to be the eternal confusion between what things are and what we call them.

When a router does lookups from a static table it's "static routing".

When Java does lookups from a static table it's "dynamic dispatch".

The same type of computation is being characterised as both "static" and "dynamic".

When a router does lookups from a dynamic table it's "dynamic routing" - there is no equivalent in Java because making the dispatch table reflexive/mutable is precisely what violates type-safety!

Re: Parse, Don't Validate (2019)

#258
post #77
post #68

Earlier quoted context omitted.

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…

I mean, GP said "you can't have routers" and maybe I'm being dense by interpreting that as "never or almost never," but even with a generous "too hard to be practical," I still don't think it's correct. And I explicitly said "escape hatch" meaning language feature. You don't need that much indirection to get routers in Haskell, Rust, Go, C, C++... like I fail to see how implementing routers are a barrier in strict ty…

What C++, Haskell, Rust, Go etc. call "dynamic dispatch" is what a router calls "static routing". The defining characteristic is that the lookup table is pre-determined and immutable at runtime.

What routers call "dynamic routing" is having the lookup table mutable at runtime.

There can be no equivalent to that in type-safe languages because when you mutate the dispatch table you lose type-safety.

Re: Parse, Don't Validate (2019)

#259

Earlier quoted context omitted.

For what its worth: People don't use dynamic language because they don't know better or never used a static language. To better understand what dynamic languages bring to the table, here are some disadvantages of static types to consider: Static types are awesome for local reasoning, but they are not that helpful in the context of the larger system (this already starts at the database, see idempotency mismatch). Code…

This argument is common, but I've never understood how a dynamically typed language is supposed to avoid coupling algorithms to data structures. When using a data structure, I know what set of fields I expect it to have. In TypeScript, I can ask the compiler to check that my function's callers always provide data that meets my expectations. In JavaScript, I can check for these expectations at runtime or just let my f…

If you are doing things with what is basically strings, as you find a lot of user input from web form inputs is, the advantages in having a lot of different strict types of Strings isnt huge. In these scenarios, using just basic types gets you a long way, because there is often very little business logic - route here, render that template, set these attributes.

Even a lot of JSON or XML parsing, you throw it into a parser and take what you need; if an unrelated field isnt what you expected, just move on with things rather than stop everything because the library author forgot about extension or openness possibilities (ie: an xs:any in a schema).

This attitude comes from the assumption not that types are unhelpful; just the chances we've modelled every outcome into our view of the world and gotten that right is unlikely.

When you get to complex systems with state changes to data and strict, well defined policies, rules engines, etc? Thats where dynamic languages often start adding all of that validation layer, to assert for right now you should act more like a type system and it's important - it probably has financial or security or other risks.

Re: Parse, Don't Validate (2019)

#260

Earlier quoted context omitted.

For sure. I do most of my work in situations of high volatility of domain and requirements and relatively high risk. (E.g., startups, projects in new areas.) Static typing really appeals to me on a personal level. I enjoy the process of analysis it requires. I love the notion of eliminating whole classes of bugs. It feels way more tidy. I took Odersky's Scala class for fun and loved it. But in practice, they're just…

> But in practice, they're just a bad match for projects where the defining characteristic is unstable ground. They force artificial clarity when the reality is murky. The rebuttal to this is provided by another post from Alexis King: https://lexi-lambda.github.io/blog/2020/01/19/no-dynamic-typ... > This story sounds compelling, but it isn’t true. The flaw is in the premise: static types are not about “classifying th…

In this case, the interview problem was a simplified version of a thing the team gets asked to do. So I believe it was as representative as possible for an hour's coding.

I disagree that "real production code" is not throwaway. In situations of high requirements volatility, a great deal of production code ends up getting thrown away, because either a) it's intended to be a short-lived way to learn about the domain, or b) it's built on an assumption that turns out to be incorrect. And until the project hits economic sustainability, a lot of assumptions necessarily get made. It's only once the project is economically sustainable that we have some confidence that the code will be around for the long haul.

If your point is that most people are using statically typed languages wrong for high-volatility situations, I'll take your word for it. There were definitely times Typescript users seemed to be unnecessarily constraining things, for example. But I'm not sold that the more elaborate approach of being thoughtfully and explicitly untyped for a project's early stages for the sake of prototyping in a statically typed language would yield much practical gain.

Post reply on HN