DEV Community

Cover image for Do your Values align with FP Values?
Zelenya
Zelenya

Posted on

Do your Values align with FP Values?

šŸ“¹Ā Hate reading articles? Check out the complementary video, 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

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Ā ā€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 and formal methods).

Formal methods

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


head :: NonEmptyList a -> a
Enter fullscreen mode Exit fullscreen mode

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

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;
[
  {
    "inventory": {
      "shoes": [
        {
          "name": "CHUCK TAYLOR ALL STAR LIFT",
          "brand": "Converse",
          "stock": 4
        },
        {
          "brand": "Morrison"
        }
      ]
    },
    "location": "London"
  },
  {
    "location": "Berlin"
  }
]
Enter fullscreen mode Exit fullscreen mode

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;
_allShoes = 
--  warehouses . traverse . inventory . traverse . shoes . traverse
  warehouses . _Just . traverse . inventories . traverse . shoes . traverse

_existingStock = _allShoes . stock . _Just

over _existingStock (subtract 1) warehouseResponse
Enter fullscreen mode Exit fullscreen mode

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:

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
Enter fullscreen mode Exit fullscreen mode

or

type Lens s t a b = forall p. Strong p => Optic p s t a b
Enter fullscreen mode Exit fullscreen mode

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: 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Ā andĀ Boring Haskell), 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ā€

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

State of Haskell Survey Results

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.

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


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, the 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


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



References:

  • Scott McCloud, ā€œUnderstanding Comicsā€

Top comments (1)

Collapse
 
webbureaucrat profile image
webbureaucrat

Elm is the most perfectionist language I'm aware of except for Idris. I say that because there are only two ways to get a runtime exception in Elm.

  • bad JavaScript port
  • infinite mutual recursion