Do your Values align with FP Values?
Tags: functional, haskell, scala, rust
Published: 2023-08-16 23:52:48

📹 Hate reading articles? Check out [the complementary video](https://youtu.be/co-Vg7M4yKw), which covers the same content.

---

I saw a tweet once saying, *“FP doesn’t worth it. It’s very time-consuming, hard to understand, hard to read. I’m fine with some bugs caused by mutability and side effects”*. I was so happy to see it; it’s a great filter.

Like other “technologies” and things in general, FP is not for everyone. When choosing a job or picking a community, values matter.

It’s vague and not explicit, but I gravitate to FP (languages, concepts, communities, and so on). I know that under this umbrella, I’ll find people and jobs with similar values. While working with others doesn’t bring me joy.

To clarify: It’s not about fake corporate values like loving our customers, transparency, and others. I wanna talk about actual things we care about.

![A grain of salt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ijph0zn7dom9347e1vms.png)

Disclaimer: I can not speak for everyone; there is no one FP and no FP community. My experience comes from various places, as I’ve been in the pot for a few years: FP conferences, online communities, and jobs (in Europe, the US, Haskell-first, Scala-first... companies). For the purpose of this topic of **FP values**, I’ll cover the most common trends I’ve observed and experienced.

## Do the types and values align

Naively we might say that all languages and communities care about:

- Performance
- Security
- Multiplatform support
- …

Obviously, nobody wants to write not-performant, insecure code. Right? 

It depends. Because you can’t have it all.

For instance, there are operating systems specifically focused on security and have to sacrifice performance, out-of-the-box experience, some hardware-compatibility, or all at once.

On the contrary, what language comes to mind when you think about simplicity? Would it sacrifice performance and security to make things simple? Probably. Because simplicity is their **core value**.

So, what are the **core** FP **values**? 

## Core FP values

### Curiosity

I’ll start with a personal favorite: **curiosity**, which is the coolest part of our job. There are so many different ways of doing things, so much to learn and explore. FP is even better in this regard.

For instance, there is still no standard way to organize an app or handle errors. Some might find it annoying. For me, it’s the opposite! We’re not stagnating — we’re constantly trying to come up with better approaches. We don’t want to settle on [23](https://en.wikipedia.org/wiki/Design_Patterns) ”design patterns” and use them for everything for years.

We don’t have to do what everyone else does; we can find our way.

> I also want GHC to be able to innovate … I think innovation is a foundational part of Haskell’s attractiveness and culture
> 
> — Simon Peyton Jones (a lead developer of the Glasgow Haskell Compiler)
> 

Language and library developers are on the same page — constantly innovating. Both Scala and Haskell are (at least partially) research languages. Not everything sticks — someone’s **curiosity** and research paper is someone else’s maintenance burden, but you don’t know if you don’t try.

There is another tiny problem here. The Aha moments! What a joy. Most people, when they learn something, get a dopamine kick, which might lead to an addiction. Which might lead to devs getting high on math and failing to deliver.

### Robustness

We don’t like null-pointer exceptions, compiler-preventable bugs, and waking up in the middle of the night. We prefer to write **robust** code.

---

💡 Note that even the core values aren’t equal. For example, some people value robustness less than **curiosity** (for example, in research or academia).

---

And it comes at a cost. In FP land, you can’t pretend that only the happy path exists — you have to deal with the unexpected: optionality (missing values), handle errors, be explicit about I/O, etc.

Other values aside, Haskell and Rust are quite beloved by blockchain and fintech startups. Using less error-prone language contributes to the reliability and robustness of the code we write.

Additionally, FP devs accept more rigorous testing techniques and analysis (such as [property-based testing](https://youtu.be/FS_VJOQfogc) and formal methods).

![Formal methods](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/aq7f9fx7yf635lgxti9s.PNG)

If it seems tedious, remember that mistakes in code can be expensive and harmful. At the same time, we write the code that we have to **maintain** tomorrow, next week, next quarter…

### **Maintainability**

On top of that, we don’t like one-off scripts, boilerplate, poor types (we prefer rich type systems), and invariants outside of code (in the comments or developers’ heads). **Robustness** and **maintainability** come hand in hand. The more robust the code is today, the easier it is to maintain it tomorrow. 

**Maintainability** is one of the cornerstones of FP and far more valued than in other communities.

![Maintainability costs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jl9pc2yt9nb3zqhe2vvm.png)

---

```haskell
head :: NonEmptyList a -> a
```

*In this example, we explicitly state that the list should not be empty. It’s in the function’s interface — not in the comment or assumed in the function’s body. We must ensure we’ve constructed a valid list when we call the function.*

---

Refactoring is easy — there are no whopper frameworks or inflexible design patterns. We don’t have to worry about the global state or assumptions.

> Refactoring is cheap in Haskell so you don't need to get things right the first time
> 
> — [Gabriella Gonzalez](https://www.haskellforall.com/2017/10/advice-for-haskell-beginners.html)
> 

You don’t have to be afraid of changing your codebase!

We also love **composability**. When you have small, independent, and composable pieces, reasoning about them is more straightforward. It lets us develop them in isolation while simultaneously building larger components from smaller ones.

### Expressiveness

We don’t have to suffer pursuing **maintainability** and **robustness**, when we have powerful tools. Most of the FP languages and libraries are quite **expressive**.

This is my favorite example from work experience: we had a data transformation pipeline, which accepted warehouses with stocks, and our task was to update all existing shoe stocks. At some point, the requirements changed, and the input data was remodeled.

---

💡 The actual details and code don’t matter, don’t stress over it. I’ll cover this in detail somewhere else in the future.

---

**Before:**

- there is a list of warehouses with inventories;
- `inventory` is optional;
- `name` and `stock` are also optional;

```json
[
  {
    "inventory": {
      "shoes": [
        {
          "name": "CHUCK TAYLOR ALL STAR LIFT",
          "brand": "Converse",
          "stock": 4
        },
        {
          "brand": "Morrison"
        }
      ]
    },
    "location": "London"
  },
  {
    "location": "Berlin"
  }
]
```

**After:**

- the `warehouses` field of response is optional now;
- there are multiple inventories per warehouse (the `inventory` field became `inventories`).

**Code changes:**

- add one `_Just`;
- change `inventory` to `inventories`;

```haskell
_allShoes = 
--  warehouses . traverse . inventory . traverse . shoes . traverse
  warehouses . _Just . traverse . inventories . traverse . shoes . traverse

_existingStock = _allShoes . stock . _Just

over _existingStock (subtract 1) warehouseResponse
```

The changes in our code were quite minimal because we were using flexible tools.

That’s why FP languages are fantastic for prototyping. It’s an industry standard that most prototypes go to production. In which case, we’re safe and ready to go. Otherwise, adapting the code to catch up with changing requirements is fast.

Another side of the coin is **over-abstraction**. Let’s revisit optics as an example:

```haskell
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
```

or

```haskell
type Lens s t a b = forall p. Strong p => Optic p s t a b
```

These are very expressive types. But also painful to work with, especially as a beginner: it’s not easy to grok, and errors aren’t friendly at all (as the types `s t a b` suggest).

Another example is Scala’s [http4s](https://http4s.org/v1/docs/dsl.html): `HttpRoutes` is “just an alias” for a Kleisli. Which sounds cool. As cool as taking an ice bath. It’s too much. I just want to send a couple of jsons over the wire.

There are dozens of other examples, and it can be very frustrating.

---

Sometimes **expressiveness** leads to **simplicity**, but sometimes it leads to complexity. We’ll talk about this later.

### Perfectionism

Now, last but my least favorite core value: **perfectionism**, which, for example, can come from obsession over **robustness** or over-pursuing **expressiveness**. Scala? Not expressive enough! Haskell? Not expressive enough! Dependent types? Not enough …

This can be discouraging, especially in the work environment. **Perfectionism** is stressful, expensive, and counterproductive. While dreaming of perfection, we end up with nothing instead of having something good or great.

Some FP devs would rather spend weeks reimplementing a library than use or adapt an imperfect existing one. Others would start working on the compiler (existing or new) just because a feature is quirky. And then people burn out cause it’s a lot of work and pressure.

But it’s not all bad. I’m grateful I can use all the polished software (if it ever gets published).

Many libraries widely used in production have been stuck on version `0.+` for years because authors think that the API is not perfect. Which I guess is the compromise.

## Interlude

What can be done with Java, can be done with Go, and can be done with Haskell. You don’t need to do FP — it’s not ultimately better. Nothing is. If the person goes out of their skin trying to convince you that X is ultimately better than Y, they are probably biased; it has something to do with what they deeply care about. If you’ve ever seen companies doing “rewrites”, you’ve probably noticed that the reasons are rarely purely technical. Is it politics? Or is it values related?

> We may say, therefore, that modern technology has deprived man of the kind of work that he enjoys most, creative, useful work with hands and brains, and given him plenty of work of a fragmented kind, most of which he does not enjoy at all…we might do well to take stock and reconsider our goals.
> 
> ― E.F. Schumacher, Small Is Beautiful: Economics as if People Mattered
> 

Why do anything? Just for the money? For financial stability or job security? Then the rest doesn’t matter — pick the most-mainstream-top-ranked tech and call it a day. “Nobody ever got fired for choosing IBM”, right?

Or is there more to it? Intellectual challenge? Growth? Community? Innovation? Sustainability?

When I started programming, it was fun — it fulfilled my creativity and curiosity. And it still does, but at some point, it became less romantic: ceremonies, JIRA, eye-rolling meetings, wobbly requirements, and so forth. And when I get a moment to code, I have little patience for null-pointers, boilerplate, silly bugs, archaic design patterns, semicolons on every line, and other nonsense.

## Not FP values (yet?)

Let’s go through some values that a developer might expect but probably won’t get from FP because they clash with the core values or are just low on the priority list.

### **Growth**

The first one is evident based on the popularity and state of FP: there’s minimal effort put into marketing and **growth**.

You might have heard of Haskell’s motto: “Avoid Success-at-all-cost”. We don’t want to sacrifice **robustness**, **perfectionism**, and the ability to innovate (**curiosity**). Mainstream approval doesn’t worth it.

And it seems like most devs don’t mind. Small is beautiful. Til you have to look for another job 😓

### **Beginner-friendliness**

In some sense, **growth** is linked to **beginner-friendliness** (or **approachability**). Hard to grow if the onboarding experience is challenging.

- Little (or no) documentation and learning materials.
- General complexity of things.

(Just recall what we talked about in the **Expressiveness** chapter).

[In a sarcastic tone] Of course, we want more users. But if the choice is between making something **approachable** or more **abstract** (for example), the cool abstraction always wins. Also, some say it’s not an option to diminish the language or library to make things easier for new users.

I think it’s just excuses. There are no technical sacrifices here. Just time and effort. Plenty of languages and technologies manage to be quite friendly to beginners on top of their “more technical” values. I hope the situation gets better sooner than later.

### Simplicity

As I alluded to earlier, sometimes **simplicity** gets a hit. This value is still up for debate. Occasionally, there are initiatives to commit to **simplicity** (for example, see [Simple Haskell](https://www.simplehaskell.org/) and [Boring Haskell](https://www.snoyman.com/blog/2019/11/boring-haskell-manifesto/)), but they have yet to stick around.

Again, notice how it conflicts with current core values.

For instance, it’s hard to predict where **curiosity** will lead you. And it’s hard to resist weird abstractions, quirky unmaintained libraries, and experimental compiler extensions.

![Scott McCloud, “Understanding Comics”](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cfhrbovomn6fihn4p1dt.jpeg)

The languages, tools, and libraries have a lot of complexity. When we use **abstractions**, we hide some complexity, making it easier to use. But easy isn’t **simple** — complexity is still there, just hidden.

The simplest code is no code because code is a liability. But this fact is generally neglected. I think because it’s fun to write functional code, so YOLO.

### Stability

Another disputed value in FP is **stability**.

Haskell and Scala have their roots in academia, so **innovation** is crucial to them.

So, on the one hand, they don’t want to restrict the research possibilities for the sake of corporate users who wish for **stability** and no breaking changes.

Every year, [State of Haskell Survey Results](https://taylor.fausak.me/2022/11/18/haskell-survey-results/) shows that upgrading the Haskell compiler breaks a significant amount of code, and people would rather stay on older versions.

![State of Haskell Survey Results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fo14v38yelcikx69uxs2.png)


![State of Haskell Survey Results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nb02684y2ly91oux2n9b.png)

A similar situation in Scala. Scala 3 has been around for years, but still, a lot of companies stay on Scala 2 because of the breaking changes — the migration is [out of reach for some](https://www.reddit.com/r/scala/comments/13q0uly/migration_to_scala_3_what_does_not_work_yet_for).

But somehow, on the other hand, standard libraries (e.g., Haskell’s base) are “too” stable — they are full of unsafe and slow functions that developers should generally avoid. So why do we keep them? Because we afraid to break all the learning material that relies on it? I don’t know. How many books are there anyways?

### Debuggability

Last and the least, let’s talk about **debugging**. The funny thing about debugging ergonomics: it’s not that FP devs don’t debug or like to suffer doing it; it’s just not something we do that often.

Once more, it’s all tied to the core values: the more time you spend on **robustness** and **perfecting** your code, the less time you have to waste **debugging**. And if we need to find a bug, we have [other tools and approaches](https://www.youtube.com/watch?v=iAzaShuVpPc). Functional code is composable — we don’t need a “real” debugger; we can inspect code snippets in isolation.

> The principal problem with debugging is that it doesn’t scale. (…) in order to catch bugs, we often need to be able to run with sufficiently large and representative data sets. When we’re at this point, the debugger is usually a crude tool to use (…) Using a debugger doesn’t scale. Types and tools and tests do.
> 
> — [Ben Deane](https://www.elbeno.com/blog/?p=1598), an experienced game developer who worked on Goldeneye, Medal of Honor, StarCraft, Diable, World of Warcraft, and so forth
> 

---

🔗 I took this quote from Daniel Lemire's [I do not use a debugger](https://lemire.me/blog/2016/06/21/i-do-not-use-a-debugger/)

---

## A few cases

Before we wrap this up, I want to reemphasis that these values are not some noble truth. 

---

⚠️ Note: I’m only focusing the values that I’ve covered.

---

### Elm

For example, Elm has a different view on these, emphasizing **beginner-friendliness** and **simplicity**. This works well for their specific use-cases. At the same time, it’s limiting and comes at the cost of expressiveness and curiosity, which irritates a big group of FP devs. Just try calling an effectul JS function out of Elm.

I’m not sure about **perfectionism** and **stability**. The compiler hasn’t had a release since 2019. And elm-devs are quite content with this. I don’t know; I don’t have enough knowledge here.

---

- ⬆️ Beginner-friendliness
- ⬆️ Simplicity
- Robustness
- Maintainability
- ~~Curiosity~~
- ~~Expressiveness~~
- ❓ Perfectionism
- ❓ Stability

---

### Rust

---

💡 Note that Rust isn’t really an “FP language”, but FP devs gravitate towards it, and you might guess why based on the values.

---

Rust is another ecosystem that strives to be **beginner-friendly**. If it actually is doesn’t matter. What matters is that they focus on and prioritize it. Most libraries come with a book, and the over-abstraction is not endorsed. For example, instead of having one **expressive** concept to deal with optionality, errors, lists, and async/await, Rust has different constructs for each. It’s annoying for some FP devs but great for beginners.

I don’t have to tell you about Rust’s **growth, t**he hype train is blazing. You must live under a pile of rocks if you haven’t heard of it.

On top of **robustness**, the community aims to get shit done — there is no unhealthy perfectionism we’ve talked about. I love it. There are production-ready libraries for everything.

But because **robustness** is so much valued, **maintainability** suffers. I might be biased, but I’ve heard it from more than one Rust dev. When you start working on a new feature, if you pick a wrong approach, it’s pretty penalizing to go back and change anything down the road. Changing ownership and lifetime in one place bubbles up through all the usage.

Sometimes you can't even swap two seemingly-innocent lines because they have to be in a specific order to satisfy the borrow checker.

![borrow checker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p37woo6b8grxqn0t61cm.png)

---

- Robustness
- ⬆️ Beginner-friendliness
- ⬆️ Growth
- ❓ ~~Maintainability~~
- ❓ Curiosity
- ~~Expressiveness~~
- ~~Perfectionism~~

---

## In summary

The work that we do and the technologies we use are reflections of our values. And as you might have noticed, you can’t have them all.

If you haven’t done it recently, I encourage you to (re)examine your values. What are your values? Does your work align with your values?

And if you’re doing FP, what do you think of its values? Am I missing something? I’d like to hear about other perspectives.

---

{% embed https://youtu.be/co-Vg7M4yKw %}

--- 

**References:**
- Scott McCloud, “Understanding Comics”