Live data from Hacker News

A couple million lines of Haskell: Production engineering at Mercury

blog.haskell.org

71–80 of 249 posts

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

#71
post #6

It’s hard to imagine what two millions lines of Haskell could possibly be doing. I mean that’s a lot of code and I have the impression that Haskell is “tight” meaning a little code can do a lot. Maybe they have a lot of libraries to do things like json serializing/deserializing, rest api frameworks, logging etc?

> Haskell is “tight”

Absolutely not an objective metric, but I have found that Haskell just has a different "aspect ratio". Line count may be somewhat lower, but the word count is essentially largely the same as more imperative OOP languages.

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

#72
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 is a very unusual Rust experience. I find "application code" very pleasant to write in Rust. Of course there are things that aren't as ergonomic in Rust as in other languages (e.g. callbacks) but that's true of pretty much any language.

I have heard this reaction from others before. One of the Rust expert friends I consulted with told me "I'm not convinced you're not trying to write Haskell-style code in Rust;" I told him the patterns I was struggling with were both trivial and common in Java.

The things I found quite difficult or impossible in Rust were to me pretty basic patterns for modularity and removing duplication that it's really shocking that these complaints are not more common.

I currently have but two hypotheses for why.

First, the second problem I mentioned only comes from using tokio, which causes your top-level program to secretly be using a defunctionalized continuation data type, derived from where exactly in other files you put your await's, that might not be Send. If you're not using tokio, you won't experience that issue.

Second...I was kinda told to just give up on deduplication and have lots of copy+pasted code. This raises the very uncomfortable hypothesis that 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, and people who were raised on traditional good software engineering but then got hit with Rust's metaphorical baseball bat of lack-of-modularity over and over until they got used to being hit with a baseball bat as a normal pain of life.

I don't like either of these explanations (esp. with tokio seeming quite dominant), so I'm awaiting an explanation that makes more sense. https://xkcd.com/3210/

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

#73
post #63
post #47

I loved working in Haskell for a few years. I wasn't actively looking it, but the opportunity just sort of landed in my lap. It was exciting and mentally stimulating. But the unfortunate fact is, I am easily twice as productive in Rust as I am Haskell, even after 3 years of nothing but Haskell. There are more pitfalls in Haskell that you have to just know how to avoid. It can be very difficult to digest as the langua…

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…

(1) Higher order functions are pretty much the same as all the other languages you mentioned, using closure syntax? What was the problem you ran into?

(2) In such situations the compiler (type system or borrow checker) is telling you that what you wanted to do has hidden bugs, and therefore refuses to compile. Usually that's a good thing.

(3) &dyn Trait

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

#74
post #62
post #55

Earlier quoted context omitted.

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

Are type classes scary? PHP has had them since 2012.

They are different things.

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

#75
post #70

Earlier quoted context omitted.

> There's a performance cost to this That part is (de facto) required for dynamically typed languages, but not for statically typed ones where the newtype constructor/deconstructor can be elided at compile time. Rust and C++ especially both do the latter by having true value types available for wrappers that evaporate into zero extra machine code. But then just this moment I wondered: do any major runtimes using mode…

Well, java can do escape analysis, so a wrapper with a single field may end up as a local variable of the embedded field. As for other JVM languages like Kotlin and Scala, they have basically what "newtype" is, but it can only be completely erased in the byte code when they have a single field.

Escape analysis that sinks a local allocation is great in itself, but for newtypes for things like “trusted HTML vs plain text”, I feel like the primary benefits are deeply interprocedural. The type constraint is encoding a promise that can be carried from one end of the code base to the other, and where you can know for sure when you're writing a module whether you're on one side of a barrier or the other. I would tend to expect this to result in patterns that aren't well-handled by escape analysis.

What I'm imagining for my curiosity about the dynamic case would look more like “JS/Lua/whatever engine detects that in frob(x) calls, x is always shaped like { foo: ‹string› } and its object identity is unused, so it replaces the calling convention for frob internally, then propagates that to any further callers”, and it might do the same thing when storing one of those in fields of other objects of known shapes, etc. until eventually it hits a boundary where the constraint isn't known to hold and has to be ready to materialize the wrapper object there.

Kotlin and Scala sound like they're doing the Rust/C++ thing at the bytecode level, if it's being “erased”, so just the static case again but with different concrete levels for machine vs language.

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

#77
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..…

Admittedly I don't really understand your construction. But this solution, if it works, doesn't look practical enough that it could be routinely used in practice like Foo|Null could be. By the way, some languages even shorten "Foo|Null" to "Foo?" as syntax sugar.

> but you get to keep `null` away from your code...

I don't think this would be desirable once we have eliminated null pointer exceptions with untagged unions.

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

#78
post #63
post #47

I loved working in Haskell for a few years. I wasn't actively looking it, but the opportunity just sort of landed in my lap. It was exciting and mentally stimulating. But the unfortunate fact is, I am easily twice as productive in Rust as I am Haskell, even after 3 years of nothing but Haskell. There are more pitfalls in Haskell that you have to just know how to avoid. It can be very difficult to digest as the langua…

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…

I appreciate Rust for making affine types mainstream, and having at least the C++ community start caring about security, even if half hearted.

However I share your conclusion, outside scenarios where having automated resource management as the main approach is either technically impossible, or a waste of time trying to change pervasive culture, I don't see much need for Rust.

In fact those that write comments about wanting a Rust but without borrow checker, the answer already exists.

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

#79
post #72

Earlier quoted context omitted.

That is a very unusual Rust experience. I find "application code" very pleasant to write in Rust. Of course there are things that aren't as ergonomic in Rust as in other languages (e.g. callbacks) but that's true of pretty much any language.

I have heard this reaction from others before. One of the Rust expert friends I consulted with told me "I'm not convinced you're not trying to write Haskell-style code in Rust;" I told him the patterns I was struggling with were both trivial and common in Java. The things I found quite difficult or impossible in Rust were to me pretty basic patterns for modularity and removing duplication that it's really shocking th…

> 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 as a Haskell user of all things you must understand that sometimes making things harder is worth the trade-off. Yeay everything is pure! Great for many reasons. Now how do I add logging to this deeply nested function?

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

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

Maybe it depends on the application, but web servers are effortless with something like axum. Libraries can do a lot of heavy lifting to expose straightforward coding patterns. Never had any problems like you desribed with database connections and such. In rust with db pools things just work and get closed on drop etc. I would never even consider making a higher order function for that. Only other language that I thi…

Depends very much on the market.

On my line of work we don't do Web servers from scratch, we use lego pieces like with enterprise integrations.

Think Sitecore, Dynamics, Sharepoint, Optimizely, Contentful, SAP, Mongolia, Stripe, PayPal, Adobe, SQL Server, Oracle, DB2,.....

Axum offers very little over existing .NET, Java, nodejs SDKs provided by those vendors.

Post reply on HN