1,762 karma · joined February 24, 2014
There are also some common patterns for less idiomatic but more performant code that you can use as a first pass when you need to optimize things further. Usually that’s sufficient, but when you need to go even further with optimization then it can get hard. Every language can. In Haskell, it usually means starting by dumping core and seeing what the compiler is doing, and getting familiar with the different passes the compiler makes. As a last resort you can also just write in C and use the ffi.
Use modules as your basic unit of abstraction. Don’t go out of your way to make their organization over-engineered, but each module should basically do one thing, and should define everything it needs to do that thing (types, classes, functions).
Use parametric polymorphism as much as you can, without making the code too hard to read. Prefer functions and records over type classes as much as possible. Type classes that only ever have a single instance, don’t have laws, or type classes defined for unit data types are major code smells.
Don’t worry about avoiding IO, but as much as you can try to keep IO code separate from pure code. For example, if you need to read a value from the user, do some calculations, then print a message, it’s far better to factor the “do some calculations” part out into a pure function that takes the things you read in as arguments and returns a value to print. It’s really tempting to interleave logic with IO but you’ll save so much time, energy, and pain if you avoid this.
Essentially, keep things as simple as you can without getting belligerent about it. The type system will help you a lot with refactoring.
Start at the beginning. Write functions. When you see some piece of functionality that you need, use `undefined` to make a placeholder function. Then, go to your place holder and start implementing it. Use undefined to fill in bits that you need, and so on.
Fancy types are neat but it’s easy to end up with a solution in search of a problem. Avoid them until you really have a concrete problem that they solve- then embrace them for that problem (and only that problem).
You’ll refactor a lot, and learn to have a better gut feeling for how to structure things, but that’s just the process of gaining experience. Leaning into the basics of FP (pure functions, composed together) will be the path of least resistance as you are getting there.
> In Haskell, how is that handled? do I have to recreate the list of entities with their changes at every simulation step? does Haskell have a special construct that allows for values to be overwritten, just like in imperative languages?
You don't _have to_ recreate the list each time, but that's probably where I'd suggest starting. GHC is optimized for these kinds of patterns, and in many cases it'll compile your code to something that does in-place updates for you, while letting you write pure functions that return a new list. Even when it can't, the runtime is designed for these kinds of small allocations and updates, and the performance is much better than what you'd get with that kind of code in another language.
If you decided that you really did need in-place updates, then there are a few options. Instead of storing a vector of values (if you are thinking about performance you probably want vectors instead of lists), you can store a vector of references that can be updated. IO is one way to do that (with IORefs) but you can also get "internal mutability" using STRefs. ST is great because it lets you write a function that uses mutable memory but still looks like a pure function to the callers because it guarantees that the impure stuff is only visible inside of the pure function. If you need concurrency, you might use STM and store them as MVars. Ultimately all of these options are different variations on "Store a list of pointers, rather than a list of values".
There are various other optimizations you could do too. For example, you can use unboxed mutable vectors to avoid having to do a bunch of pointer chasing. You can use GHC primitives to eek out even better performance. In the best case scenario I've seen programs like this written in Haskell be competitive with Java (after the warmup period), and you can keep the memory utilization pretty low. You probably won't get something that's competitive with C unless you are writing extremely optimized code, and at that point most of the time I'd suggest just writing the critical bits in C and using the FFI to link that into your program.
> The talent pool and availability of the language
There are certainly more Javascript or Python developers out there than Haskell developers, but I think it's wrong to imply that Haskell is a hard language to hire for. There are more people out there who want to work with Haskell than there are Haskell jobs, and picking Haskell can be a really great way to recruit high quality talent. It's also quite possible to train developers on Haskell. A lot of companies hire people who don't have experience with their particular language. The learning curve for Haskell may be a bit steeper, but it's certainly tractable if you are hiring people who are eager to learn.
> The ecosystem of libraries and ancillary tools like monitoring/debugging/observability
Other languages have _more_ of these, but it's not like Haskell is missing basic ecosystem things. I actually find that Haskell is pretty nice with this stuff overall. It's not quite as automatic as what you might get with running something in the JVM, but it's not that big of a lift, and for a lot of teams the marginal extra effort here is more than worth it because of the other benefits you get from Haskell.
> The speed-of-development vs cost-of-maintenance tradeoff of the language
Haskell is really excellent here in my experience. You can write unmaintainable code in any language, but Haskell gives you a lot of choice in how you build your application, and it makes refactoring a lot nicer than in any other language I've used. You don't get some of the nice IDE features to rename things or move code around automatically, but working in a large Haskell codebase you really do start to see ways that the language makes structural and architectural refactoring a lot easier.
> But for "what language is easy to employ and has an expansive ecosystem + tooling", I feel like you have to hand it to Java, .NET, Python, TypeScript, Go, etc...
Those are all perfectly good choices. I think what people tend to overlook is that Haskell is _also_ a perfectly good choice. Everything has tradeoffs, but Haskell isn't some terrible esoteric choice that forces you to leave everything practical on the table. It really is useful day to day as a general purpose language.
Personally, I have mixed feelings. I think that most of the outcomes we're most concerned about are going to happen one way or another, and developing in public or even commoditization of access to it is going to be a net reduction in harm over locking it up and pretending it doesn't exist while allowing people (and nations) with the resources to run large models in secret to develop and use the technology against people who are ignorant of what's possible.
Movies and TV:
- As an alternative to motion capture for animation
- As an alternative to existing de-aging CGI when you want to flash back to a younger version of a character (especially for cases where newer sequels are being made for much older movies)
- As an easy way to get some additional footage if an actor no longer looks the part
In a professional setting: - Conduct job interviews were interviewees faces are mapped to the faces of a few pre-defined images, to reduce a major source of implicit bias in interviewing
- Get some footage of yourself when you're looking your best, with great lighting, and use that rather than being off-camera if you're joining a meeting when you don't look great
- Create virtual spokespeople to represent your company in marketing, and allow that person to be played by different actors
News and Politics: - An alternative to blurred or blocked out faces for people giving interviews or whistle blowing
- Allow people to testify in court (virtually) without revealing their identity and risking retaliationFor extremely optimized Haskell you can get close to the speed of C, but there’s still a garbage collector.
There are also certain classes of problem where a naive Haskell implementation can beat other languages by mile, including C, if you use the same implementation in both languages. Laziness can be really great sometimes. This didn’t happen much in practice though because the kind of code that’s really efficient with lazy evaluation is very obviously not in a strict language so people don’t usually write code that way.
In the end I’d say Haskell is a good choice for performance sensitive but not performance critical program. In a larger Haskell application if you have a performance critical bit you can usually write Haskell code that will be fast enough if you know what your doing. For something stand alone that needs to be as fast as possible, or the most critically performance sensitive parts of a bigger application, I’d consider using C or C++.
Instead, I think there are two far more interesting questions to ask:
1. Is the rate at which code review identifies defects sufficient to use code review as a detection mechanism for defects?
After nearly 20 years of writing software, I'm pretty convinced that the answer here is no. Some reviewers are better than others, and some circumstances are more favorable to finding defects than others, but we should generally try to build processes that don't assume defects will be caught at a substantial rate by code review. It's nice when it works, but it's not a reliable enough way to catch errors to be a load bearing part of the process.
2. Is mandatory review of all code justified?
This is the one I'm on the fence about. In an environment where code reviews are high priority, people are trained to review effectively, and there are minimal organizational politics at play, then I hypothesize that allowing PR authors to decide whether to get a review or not would generally improve quality and velocity because code would ship more quickly and code that would benefit from a review would still be reviewed. In that scenario, I think we'd see the benefits of getting things shipped more quickly when they don't require a review, and reviews would be higher quality because code being flagged for review would be a positive sign to pay more attention.
Unfortunately, I could be wrong, and it's not the sort of experiment anyone wants to risk their reputation pushing for, so I doubt we're likely to see an experiment at a large enough scale to know for sure. If we're going to fail one way or another, I'd prefer to fail by doing too much code review rather than not enough.
My book, Effective Haskell (https://pragprog.com/titles/rshaskell/effective-haskell/), is a pretty well reviewed book on a fairly niche technology. It's been in print for about a year, and was in beta for around 6 months before that. I'd estimate I spent around 4,500 hours over 5 years writing my book and the royalties so far mean that the value of that time was around $5/hour pre-tax. That hourly rate will go up slowly over time until the book stops selling, but it's pretty hard to argue that it's objectively worth it just for the royalties.
My motivation for writing the book was never the money, and I've generally treated the royalties as a nice bonus. I started writing because I cared a lot about the technology, and I wanted to share it with other people. Writing the book was my way of contributing something to a community that I'd benefited from a lot in my career.
In addition to the satisfaction, there have been other benefits I've seen to writing a book. It's opened up opportunities for other side income. I've had paid speaking opportunities, and I've been invited as a guest lecturer at a couple of universities and been offered to chance to design and teach a course at a well ranked local university. If I wanted to actively pursue consulting as a side gig I think the reputational gain would help me there as well.
Writing a book has also helped me in my career. In a direct way, I think the benefit to my reputation helped me get interviews. The communication and technical writing skills I gained writing a book have also helped me as I've moved into a leadership role. It's impossible to know precisely how much writing a book contributed or to put a clear monetary value on that, but I do think it's contributed.
The other side of this is that, a year after finishing writing on my book I'm still recovering a bit from the burnout of working so intensely on a side project for so long. I still have some follow-up work (extended solutions to all exercises, errata, fixing up and publishing some cut chapters as free content) that I intend to take on and it's been really hard to find the energy to do it. I'd like to write a second book one day, but I'd still advise people to be mindful of the amount of work it takes and to avoid writing one unless they are absolutely certain that it's something they want to do.
On top of that, in the US a lot of time in an undergraduate program is spent on general education. Someone who’s degree is in underwater basket weaving still has more education in general than a person without a degree, and that’s also good for society.
Still, I do appreciate the perspective. The disaster preparedness and survival enthusiast in me also tries to keep a running catalogue of natural remedies in my head just in case.
I hate this sentiment. Not every degree is going to lead to a high paying career, but knowledge is worth having in it's own right. If people are passionate enough spend four years studying art history or poetry or underwater basket weaving then I'm glad we are sharing and preserving that knowledge in our culture. I don't like tipping culture, and I don't really think the two are related, but if tipping is the only way that we could get a generally well rounded educated population then I suppose I'll prefer tipping.
Honest question: why not just take aspirin and get a reliable known dose of medication instead of the random amount that you'd get with the tea?
Helping other people grow has also been important to me. Mentorship and teaching have always been things I prioritized. When I moved into a management role a couple of years ago it was because I’d done the principal level IC thing before where I was extremely cross-functional and I wanted to try focusing on a specific part of the business and a specific team of people and try to grow them. I wanted to grow vertically rather than horizontally.
I think I’m a pretty good manager, based on feed back from my team and my peers, but the reality is that it’s enormously more draining than I ever imagined and there’s very little to concretely feel satisfied about at the end of a given day. When I became a manager I thought I’d just code in my spare time since I wouldn’t be coding at work, but in reality I’m far far more burned out from any given day of management than I ever was from the most stressful day as an IC. It’s hard to find the motivation after work, and I worry about my skills slipping over time.
In an abstract sense it feels good to see a product grow, and to grow a team, but practically speaking none of the success is mine. Every good thing that gets done is thanks to the people on my team. Their growth and accomplishments are ultimately thanks to them. I facilitate, I give direction, I find alignment and build consensus, hopefully I make things easier- but I personally accomplish nothing. It feels like being stuck running at top speed on a treadmill going nowhere. Being a manager means taking on none of the credit when things go well, and all of the blame when things go poorly.
Unfortunately, part of what got me into management at all was caring about the product and the people and no matter how much it sucks giving up and going back to an IC role seems worse. I don’t want to leave my team unsupported or risk them getting a worse manager, and I don't want to see the product my team owns fail due to a lack of leadership. I’m hopeful that in a few more years with more experience I’ll figure out ways to be happier with the situation, and until then I just try to remember that most people hate their jobs and I’m just lucky to have had almost 20 years of loving mine before moving into management.
I do most of my encoding on an i9-13900k. I've also noticed that h.265 seems to be quite a bit more likely to trigger the smt related crashes in ffmpeg, which makes it a pain when I've queued up a lot of things to encode over night.
I've certainly seen people in the Haskell teams I've worked on who fit the article's description of people who were there to write Haskell and didn't care about much else. It didn't go great, but they were a minority of the people I've worked with.
Importantly, I've also seen plenty of that kind of behavior in other teams using other tech stacks. I've worked with "Agile people" whose answer to every problem is pair programming. I've worked with people who only care about microservices, or their favorite frontend framework. I've worked with people who see more object orientation as the solution to every problem more often than I've seen people who want to apply FP to every problem.
A few of these people can be find to have on a medium or larger sized team- if the worst of their instincts are tempered they can be a great source of internal education and advocacy, and they can bring expertise that can help you deal with the inevitable problems and tradeoffs that come with any technical choice. You just need to be careful to, on balance, have a team of mostly product-minded people.
Product people aren't necessarily tech-stack agnostic. To use myself as an example, I really like functional programming and I think it's often a good technical choice. At the end of the day though, my job is a job and I'm there to build the best product I can to make my employer (and myself) money. I've turned down Haskell jobs because I didn't believe in the product or team, and taken jobs in less preferred tech stacks because I did. A lot of people can be both enthusiasts and pragmatists, you just need to look for them.
I think one of the biggest issues I have with the article is that it overlooks a significant source of hiring: product minded people who are open to, but not specifically enthusiastic about your tech stack. People don't need to be an FP enthusiast to work in a functional language. I've written a lot of Python and Go in my career, even though neither of them are my favorite language. By the same token, there are plenty of people who can work with Haskell, OCaml, or a lisp just fine with a bit of training even if FP isn't something they are going to devote themselves to. I've worked with a lot of people who do Haskell in their day job, but prefer to spend their free time using Rust.
None of this is to say that everyone should go out and use an FP language. I think the most important factor in picking a language is generally going to be picking something that your team likes and understands well. Most languages are good enough at most problems that individual preference is going to matter more than technical concerns. If Haskell or OCaml or Gooby is that preferred language for your team, I don't think you should avoid it.
- Stitch: The Sound Type-Indexed Type Checker (Functional Pearl) by Richard Eisenberg
- A Criterion for Kan Extensions of Lax Monoidal Functors by Tobias Fritz and Paolo Perrone
- Effect systems revisited—control-flow algebra and semantics by Alan Mycroft, Dominic Orchard, and Tomas Petricek
- Kleisli arrows of outrageous fortune by CONOR McBRIDE
- Parametric Effect Monads and Semantics of Effect Systems by Shin-ya Katsumata
- Unifying graded and parameterised monads by Dominic Orchard and Philip Wadler
For a much more gentle introduction to some of the basic bits like using GADTs to accumulate effect annotations at the type level and building recursive type class instances to traverse them I’d recommend chapter 15 of my own book, Effective Haskell. From the examples in that chapter and the papers I listed I think you’d have everything you need to have a go at it.
There are some things you can do to make it more ergonomic. One mistake we made was not focusing on ergonomics early on, and that lead to people having some pretty sour experiences. It's something you really have to pay a lot of attention to.
We've since rewritten the system and, while we still support an optional effects based style in our DSL, it's not being used very heavily. In practice, our user found the ergonomics of the effects system quite challenging, and it introduced some type inference challenges. The biggest problem was that our use-cases ended up with functions that would have hundreds of effects, and it was fairly unwieldy and the type errors were difficult to deal with. Since we've introduced the updated version, most users prefer to user our newer features that allow them to write more traditional code even though it means they don't get composable effects.
On the other side of the experience, I've come to believe that effects systems are a good idea, but when adding them to an existing language it's probably best to make them an opt-in feature that can be used to constrain specific small parts of a program, rather than something that should be applied globally. I also think we need a bit more research into the ergonomics before they are going to appeal to a lot of users. That said, the guarantees and optimization opportunities ours gave us were really nice, and were quite difficult to achieve without building on top of the effects system (our new system is about an order of magnitude more code, for example).
People only need a computer, television, or appliance every few years at most, and I think most people tend to either make major purchases based on the lowest cost (online) or with a retailer they have an existing relationship with. Without any inventory of the kind of small frequent purchases that help build a relationship with consumers, I don't see how they have any success attracting people with big purchases.
That can be true, and I think it's particularly common with imposed, rather than adopted identities. In fact, people having particular ideas about you that aren't necessarily true is the reason a lot of imposed identities exist in the first place. There's not a lot you can really do about it. I suppose you can try very hard to not identify with anything, but that's not always an option, and even when it is it would be a very lonely existence.
> And in general, I think people who make $IDENTITY the main feature of their personality will have difficulty outside of that one social group.
Another way of saying that is that people who aren't well rounded will have trouble in a lot of social situations. I agree with that, but I don't think it's a problem related to having identities so much as it is someone restricting themselves to a single focal identity.