Live data from Hacker News

A couple million lines of Haskell: Production engineering at Mercury

blog.haskell.org

111–120 of 249 posts

Re: A couple million lines of Haskell: Production engineering at Mercury

#111
post #7

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

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…

> some pretty hacky incantations

  type NewType = T & { readonly __brand: Name };
I don't really see a big problem here?

Re: A couple million lines of Haskell: Production engineering at Mercury

#112
post #88

Earlier quoted context omitted.

What are different things?

Eli5: Haskell type classes are not classes (like Java or PHP classes); they are comparable to Rust traits -- which are different from PHP traits which are comparable to Java/C# interfaces (with default impls; if you just want contracts you have... PHP interfaces). A fundamental difference is that you can instantiate/implement a type class (or Rust trait) for any* type, compared to interfaces where each class declares…

That conflates type classes with extension types, in type theory.

Actually in modern Java you can simulate type classes approach with a mix of interfaces and default methods implementations.

In C# you can have the experience more straightforward with extensions types introduced in C#13.

Then we have yet another way to approach type classes in Scala, with traits and implicits.

And so on, as I haven't yet run out of examples.

Re: A couple million lines of Haskell: Production engineering at Mercury

#113
post #95

Earlier quoted context omitted.

> Rust afficionados are some combination of people who came to Rust early and never learned traditional software design and don't know what they're missing This is definitely not the case and is unnecessarily insulting. The truth is that some things are harder in Rust but a) often those things are best avoided anyway (e.g. callbacks), and b) it's worth the trade-off because of the other good things it allows. Surely…

> is unnecessarily insulting. I know that it's insulting! And it doesn't make sense, because I generally think Rust programmers are smart people. But right now, it's the only explanation I've got, so it is alas necessarily insulting. So please, please, please give me a better explanation that actually makes sense. > The truth is that some things are harder in Rust but a) often those things are best avoided anyway (e.…

> why are callbacks best avoided anyway

Look up "callback hell". Basically they encourage spaghetti.

> you'll actually get a solution if you ask about them

You got solutions to your problems didn't you? Macros are a perfectly reasonable thing to use in Rust, even if they are best avoided where possible. Exactly like unsafePerformIO.

If you were expecting Rust to work perfectly in every situation... well it doesn't. GUI programming in particular is still awkward, and async Rust has more footguns than anyone is happy with.

Despite that it's still probably the best language we have for a surprisingly large range of domains.

Re: A couple million lines of Haskell: Production engineering at Mercury

#114
post #7

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

“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)

Re: A couple million lines of Haskell: Production engineering at Mercury

#115

Earlier quoted context omitted.

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…

> some pretty hacky incantations type NewType = T & { readonly __brand: Name }; I don't really see a big problem here?

EDIT: previously the example in the parent comment was:

  type NewType = T & { __brand__ : Symbol }
---

This seems wrong; the type spelled `Symbol` refers to the boxed interface for symbols[0]. I suspect you meant to write `unique symbol` there, but it can't be used in that position.

I'm not sure if `NewType` in your comment is supposed to stand in for a specific newtype (in which case it probably doesn't need to be generic[1]) or if it's supposed to be a general-purpose type constructor for any newtype (in which case it should take a second type parameter to let me distinguish e.g. `EmailAddress` from `Password`[2]). The use of `unique symbol`s is also only really necessary if you want to keep the brand private to force users to go through a validation function or whatnot, otherwise you can just use string literal types.

I agree these incantations aren't big problems (it all falls out naturally from knowledge of TypeScript's type system, and can be abstracted away as per my comment in [2]), but the fact that you goofed in the very comment where you were trying to make that point is causing me to second-guess myself.

[0]: https://github.com/microsoft/TypeScript/blob/v6.0.3/src/lib/...

[1]: https://tsplay.dev/N7rvBw

[2]: https://tsplay.dev/Ndep0m

Re: A couple million lines of Haskell: Production engineering at Mercury

#116
post #7

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

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

Re: A couple million lines of Haskell: Production engineering at Mercury

#117
post #65
post #37

I know this is not the point of the article, but I find the anecdote in the beginning about null pointer errors somewhat ironic. Haskell's solution to null pointers are option types (`Maybe x` in Haskell), but these are known to be suboptimal. In languages with option types, if you want to weaken the type requirement for a function parameter, or strengthen the guarantee for a return type, you have to change the code…

>you would have to change the code at all call sites. Actually I think you can just change concrete argument `Foo` to type constraint in Haskell as well using a type class. So the function would be something like `foo :: ToMaybeFoo a => a -> .. ->`. And you would implment `ToMaybeFoo` instance for `Foo` and `Maybe Foo`. Agree that this is more involved than typescript, but you get to keep `null` away from your code..…

This is a neat idea, but it does require that you know up front the largest union that could ever be supported in that argument, so that you have the ability to narrow it down later. Worse, it in the limit it requires a combinatorial explosion of type classes, with one for each possible union! The `ToXYZorW` classes form a powerset over the available types.

Re: A couple million lines of Haskell: Production engineering at Mercury

#118
post #117
post #65

Earlier quoted context omitted.

>you would have to change the code at all call sites. Actually I think you can just change concrete argument `Foo` to type constraint in Haskell as well using a type class. So the function would be something like `foo :: ToMaybeFoo a => a -> .. ->`. And you would implment `ToMaybeFoo` instance for `Foo` and `Maybe Foo`. Agree that this is more involved than typescript, but you get to keep `null` away from your code..…

This is a neat idea, but it does require that you know up front the largest union that could ever be supported in that argument, so that you have the ability to narrow it down later. Worse, it in the limit it requires a combinatorial explosion of type classes, with one for each possible union! The `ToXYZorW` classes form a powerset over the available types.

See fundeps.

Re: A couple million lines of Haskell: Production engineering at Mercury

#119

Earlier quoted context omitted.

> some pretty hacky incantations type NewType = T & { readonly __brand: Name }; I don't really see a big problem here?

EDIT: previously the example in the parent comment was: type NewType = T & { __brand__ : Symbol } --- This seems wrong; the type spelled `Symbol` refers to the boxed interface for symbols[0]. I suspect you meant to write `unique symbol` there, but it can't be used in that position. I'm not sure if `NewType` in your comment is supposed to stand in for a specific newtype (in which case it probably doesn't need to be ge…

Right. Besides getting this incantation right, as gp did only after editing their comment, you also have to cast to create values of NewType. But generally you want to avoid casting in typescript if you care about type safety, so now everybody has to remember the rule that in this particular circumstance it's the right thing to do.

There are helper libraries to ease this (zod supports branded types, I think?), but I guess my general point is that while typescript might give you the ingredients you need to implement type safety in cases like this if you try really hard and remember all your rules everywhere, it doesn't come naturally so it's hard to maintain at scale.

Re: A couple million lines of Haskell: Production engineering at Mercury

#120
post #81
post #63

Earlier quoted context omitted.

Pretty surprising -- I had much the opposite experience. On our last product, we decided to start switching from Typescript to Rust on the backend because we got tired of crashes. I consider that to be one of the greatest technical mistakes I've made ever, as our productivity slowed massively. I'll just share two time-draining issues that only occur in Rust: (1) Writing higher-order functions (e.g.: a function to ope…

That's pretty interesting. I was thinking about starting a new pet project and was considering doing it in Rust to learn as I never tried anything with it and after some small pocs I had the feeling it was too verbose to my taste, but wasn't sure it was just me and/or my lack of experience with Rust. Still, wonder if it's still worth it to give a shot considering other positive elements of the language.

It’s worth learning, in my opinion, but I’ve been writing it professionally for the better part of a decade, so my opinion may be a bit skewed.

It’s my favorite language to write, and it gets much easier over time. As a first approximation, if you’re doing something and it feels insanely difficult like the GP is talking about, try to think of a different way to do it rather than fighting it. There’s usually a way to do almost anything, but it’s more pleasant to lean into the grooves the language pushes you towards.

Post reply on HN