logoalt Hacker News

bri3dtoday at 1:44 AM6 repliesview on HN

> Haskell gives you tools to encode these incantations in types so they cannot be forgotten. This is, for my money, the single most valuable thing the language offers a production engineering organization.

Haskell is admittedly, probably the most powerful widely (or even somewhat widely) used language for doing this, but this general pattern works really well in Rust and TypeScript too and is one of my very favorite tools for writing better code.

I also really like doing things like User -> LoggedInUser -> AccessControlledLoggedInUser to prevent the kind of really obvious AuthZ bugs people make in web applications time and time again.

I've found this pattern to be massively underutilized in industry.


Replies

germandiagotoday at 12:52 PM

I think you csn also goa long way with C++ and templates to represent sny kind of restricted type in the type system. Variants are somewhat clumsy without pattern matching but most tools you can make use of are already there I would say.

In my backend system I represent users with different variant states to avoid a lot of unrepresentable states.

As for underutilization, I think only functional languages, Rust and C++ support variants and that might be one reason: people just make blobs of state and choose which fields to use instead of encoding states and make some combinations unrepresentable. Javascript, Java, C# or Python do not have Variant types to the best of my knowledge.In Ocaml and Haskell and with pattern matching they are very natural. In Rust with enums, same. In C++, they are so so but still usable compared to the others that do not have.

In my load tests I even went, since I launch thousands of clients, with a boos.MSM to drive the test behavior. One state machine per user.

d0minetoday at 11:36 AM

“Parse don’t validate“ seems like the same idea

https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

You do not need Haskell for that eg it works in Python (via pydantic, attrs data classes)

show 1 reply
ossopitetoday at 10:31 AM

I'm not convinced it really works well in typescript. the lack of nominal types requires you to remember some pretty hacky incantations if you want something like a newtype wrapping a primitive type

my experience is that ocaml is more powerful than rust for enforcing this sort of type safety, because you have gadts that give you more expressive power, and polymorphic variants and object types (record row types) that give you more convenience. and the module system and functors of course.

you also avoid some abstraction limitations/difficulties that come from the rust borrow checker for places where garbage collection is just fine

show 2 replies
miki123211today at 3:31 AM

This isn't specific to Rust or Typescript. You can do this in basically any language.

Imagine you have to distinguish between unescaped and escaped strings for security purposes. Even with a dynamically typed language, you can keep escaped strings as an Escaped class, with escape(str)->Escaped and dangerouslyAssumeEscaped(str)->Escaped functions (or static methods). There's a performance cost to this, so that's a tradeoff you have to weigh, but it is possible.

Another way of doing this is Application Hungarian[1], though that relies on the programmer more than it does on the compiler.

[1] https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...

show 3 replies
dirkttoday at 6:12 AM

> works really well in Rust and TypeScript too

And of course Rust and TypeScript were heavily influenced by Haskell... they just don't mention it and call things differently, to avoid the "monads are scary, I need to write a tutorial" effect. Though it's less about monads and more about things like type classes.

Imitation is the sincerest form of flattery.

show 1 reply
ngruhntoday at 11:53 AM

You can't enforce purity on the type level in TypeScript and IIRC neither in Rust.

show 1 reply