如果不能正常显示,请查看原文 , 或返回

A founder's perspective on 4 years with Haskell

In 2012 I co-founded Better1, a startup building a new type of enterprise e-learning platform. Our goal was to make it faster and cheaper for large companies to develop, deliver, and analyse adaptive, cross-platform, multi-language, online courses.

From day one, we decided to use Haskell as our main language, and it remained the only language we used on the back-end as our team grew to 10 developers.

After a period of experimentation and development, Better grew from $0 to $500k+ in annual recurring revenue in the space of a few months, with companies including American Express and Swissport as customers. However, the distribution model proved challenging to grow beyond that and we eventually sold to GRC Solutions, an Australian compliance company.

Though interest in Haskell appears to be growing steadily, its use in production is still rare. Some have the mistaken impression it’s an academic language and nothing more. In this post I’ll give my perspective on what it’s like to use Haskell in a startup. Can you get stuff done? Does it hold up in practice? Can you hire developers? Should more companies use it?

The short answer to those questions is yes. It’s not a fit for all problems or for all teams, but it is worth serious consideration. For building server-side software, Haskell might be the closest thing to a secret weapon you’ll find today.2

Why did we choose Haskell?

A couple of years before founding Better, I implemented a forecasting model for a financial advisory company. A first prototype in Python turned out to be fragile and error prone. How could I ensure some variables always held dollar values and others percentages? How did I avoid accidentally mutating objects inside functions? I would need lots of tests, which would be slow and tedious to write.

I recalled Haskell from university and looked at it again. After a couple of days of experimentation, I had, to my surprise, convinced myself it was a better fit for the task and I got a rough version of the model working in about a week. Later, when we started Better I was confident Haskell would be more productive than the more common languages.

Haskell distinguishes itself from most languages by purity, laziness, and a strong, static, inferred type system. Purity lets you reason more easily about your program. Laziness allows for better function composition and optimisation (at the cost of harder-to-understand memory profiles). Strong, static, inferred types deliver a lightweight mechanism for enforcing consistency3, making refactoring a joy and eliminating a large class of bugs without the need to write and maintain tests. These properties add up to a surprisingly pleasurable and productive development experience.

Can you get stuff done?

If you’re building something from scratch, the good news is that Haskell lets you get stuff done with fairly limited knowledge. What you build just won’t be particularly neat or efficient. You might later find that something that took you 5 lines is a standard abstraction ready to be re-used.

The bad news for newcomers is that to become highly productive, there’s a lot to learn, even for experienced developers. Haskell is heavy on concepts and abstractions, though they are not as difficult as their names might suggest, for the most part. Some good textbooks exist, but blog posts and papers end up playing an important role too. This is not everyone’s cup of tea, but the concepts are valuable beyond Haskell and will affect how you think about program construction in general, which is fulfilling.

In more practical terms, one of our biggest complaints with Haskell was basic tool support. Building with cabal-install wasn’t smooth. Stack has since alleviated many of those concerns. Stack is beginner friendly and makes large Haskell projects manageable. You can do what is standard in many other toolchains, like fork libraries on GitHub and depend on the forks without having to publish the packages.

Haskell tool support is still not what it could be, in particular when it comes to IDEs and editors. It is possible to get various editor integrations set up, but it can be fiddly and there’s no commonly adopted standard.4 This is a shame, particularly given how rich and useful Haskell’s type information is.

When it comes to libraries, coverage is decent, not great. We found ourselves writing some rough internal libraries (for sending email via Mandrill’s API for example) that we wouldn’t have had to write in more popular languages.5 There are world-class libraries for Haskell, but knowing which ones are great is not always easy6 and quality varies dramatically. If memory serves me we only encountered one serious library bug, which manifested as being unable to make https connections.7

As for the language itself, Haskell’s laziness requires you to think about space complexity more than in other languages. There are good tools and techniques to help with that and you can certainly write space-efficient code, but it takes more conscious effort than with strict languages. You tend to more than reclaim that mental load by having better and more powerful abstractions at hand though. There are also a few other annoyances like converting between string types, the absence of a standard style, and the lack of a good record syntax. Lenses do solve the record problem, but they take time to learn8 and are tricky to debug.

In day-to-day use, Haskell is otherwise surprisingly pragmatic. In particular, I think it is hard to get a Haskell codebase to a point where refactorings and improvements become scary. The type system is there to help you and purity gives you much more confidence when reasoning about code.

Better ended up up building an ambitious piece of software with a couple of major rewrites and though we misjudged the market, the engineering team never failed to deliver.

Does the software hold up?

The Better platform gets around half a million learning actions every week and it has been running for well over a year with no downtime or maintenance. We still don’t know of any bugs. To top that off, we did a major refactoring shortly before we halted development of the platform.

This is only anecdotal evidence, but even I am surprised and impressed by how well the software is holding up. We had a great engineering team and they certainly deserve most of the credit, but I do believe Haskell also deserves some credit. We did not do test-driven development and even though we had tests for critical parts, we did not have anywhere near full test coverage. That seemed like the right cost/quality trade-off then and if you ask me now, it still does.

Haskell’s type system removes the need for a large class of tests required in weaker languages to catch inconsistencies. Writing and keeping such tests up-to-date is costly compared to having a strong, inferred type-system. I understand that the argument for test-driven development isn’t just about writing tests to catch bugs; it is also about helping developers think clearly about the problem and encourage a certain discipline. However, I think that Haskell’s purity and types enforce much, if not more, of such discipline and clarity. There is truth to the joke that “once it compiles it works”.

To be clear, I think testing is important. The type system can ensure certain kinds of consistency, but consistent programs can do the wrong thing. Testing is a trade-off between iteration speed and quality; different businesses require different trade-offs.

When you do test, Haskell has outstanding tools for it. QuickCheck, for example, can automatically generate a large number of random test-cases (leveraging Haskell’s type system) based on properties that the programmer says should hold for functions.

Thus, Haskell seems like a particularly good fit for software where correctness and quality matter. You might expect to pay for its safety in reduced iteration speed, but funnily enough, you could find yourself iterating faster than with more weakly typed, non-pure languages.

Can you hire people to write Haskell?

Better was based in Zurich. We didn’t know anyone here when we started, but hiring developers turned out to be easier than we had expected, in no small part thanks to Haskell. Hiring still took some effort of course, but our experience was that many developers were excited about the possibility to work with Haskell. We ended up with a great team, several of whom moved to Zurich from abroad to work with us. Early on, we made a decision to not have a remote team, but if you are open to remote development, you should have even more options.

We hired people for a mix of technical skills, interests, and motivations, depending on the role and we attracted several highly skilled Haskellers. We also had good experiences hiring people with limited Haskell knowledge. One of our developers came straight from a physics degree and quickly became productive aided by mentoring from the more experienced developers.

Although we couldn’t match Google, IBM Zurich, or the large local banks when it came to salaries, we were still able to attract great people. Haskell was one of the reasons we could do that – good developers tend to value working with good technology.

How does it affect company culture?

It’s hard to separate the effects of a programming language on engineering culture from hiring decisions, management style, and individual personalities, but I think we can attribute some of it to Haskell.

People don’t learn Haskell to get a well-paid job. There just aren’t enough Haskell jobs out there for that to make sense. The language is also perceived to be more difficult to learn than other languages, which filters out those who are reluctant to invest effort into learning about funny-sounding things like functors, applicatives, and monads.

The upside of that is that if you hire people who have spent some time learning Haskell, they are likely to have more than average intrinsic motivation to build software and to learn new things. The downside is that they are more likely to have an aversion to work that is wasteful and lacks learning opportunities.

Thus, I think choosing Haskell helped build the engineering culture we wanted, but it wasn’t magic – we also ran a careful hiring process, we tried to be fair managers, and we looked for people who could be good developers in any language.

So, should you use Haskell?

Haskell is not for everyone. It is different from what most developers are used to. It requires learning abstract concepts. Its eco-system is not as mature as some more popular languages. It is rarely the quickest language for hacking together a 2-day prototype. It has garbage collection so it is unsuitable for real-time systems. Its space-complexity can be fickle. And the number of people with production experience of Haskell is small.

But if those things aren’t deal breakers, you could be in for a real treat. Haskell is an outstanding language for building and rapidly iterating on high-quality software in small, skilled, and growing teams. As your codebase expands and evolves, Haskell’s ability to handle complexity, clarify thinking, and ensure consistency is a blessing. You will worry less about the server crashing when you go to sleep. You will spend less time on bugs in old code. You will refactor confidently and quickly. You will stand out from Java, Node, and Ruby shops when recruiting. You will start to think about program construction in new, useful abstractions. Who knows, you might even enjoy your work more.

With hindsight, there are many things I would have done differently at Better; choosing Haskell is not one of them.

Further reading

  1. The name Better is a story in itself. The reasoning behind it was that our users would get better by using our platform – our slogan was Better than yesterday. It has caused some confusion though and it makes some sentences sound odd. One of our developers spoke to Jony Ive at a convention and when he heard the name he apparently commented that “it could be worse”.

  2. In his essay Beating the Averages, Paul Graham talks about another niche language, Lisp:

    What’s so great about Lisp? And if Lisp is so great, why doesn’t everyone use it? These sound like rhetorical questions, but actually they have straightforward answers. Lisp is so great not because of some magic quality visible only to devotees, but because it is simply the most powerful language available. And the reason everyone doesn’t use it is that programming languages are not merely technologies, but habits of mind as well, and nothing changes slower.

  3. Haskell’s type system can’t guarantee that your program does the thing you intended, but at least it won’t allow you to multiply a string.

  4. haskell-ide-engine was set up with the goal to create a standard backend for Haskell IDEs, so there are people working on the problem, which is great.

  5. Shortly after we wrote our Mandrill library, the mandrill package was released.

  6. For identifying high-quality libraries, you will probably learn to recognise names of authors and maintainers that tend to produce them. Gabriel Gonzalez’s State of the Haskell ecosystem is also useful for figuring out how mature Haskell and its libraries are in different areas.

  7. The developer who investigated the library bug we encountered gives more details on Reddit. It sounds like Stack might have saved us from that as well today. Correction: An earlier version of this article claimed that the bug we encountered was in the tls library, but that was wrong as explained in the Reddit comment.

  8. lens has a steep learning curve