This content originally appeared on DEV Community and was authored by John Still
Is functional programming really worth learning? Why do so many developers give up halfway?
I personally gave up twice. But looking back, these two “failures” turned out to be one of the most valuable experiences in my programming journey.
1. First Encounter: Haskell’s Illusion and Collapse
I’ve been writing Python for over three years. I love its clean syntax and the “just run it” simplicity.
But in the community, I kept hearing one narrative:
Functional Programming (FP) is the real elegance.
It’s pure, bug-free, and will change the way you think about code.
So I picked the “purest” language I could find — Haskell.
My expectations:
- Code would look like math formulas.
- No messy state, no hidden bugs.
- Elegant, academic, cutting-edge.
Reality:
- I stumbled even at “Hello World.”
- I had to understand
Monad
before I could print to the screen. - Want a loop? Sorry, there are no loops in Haskell — only recursion,
map
, andfoldr
.
For example, in Python I could write:
def greet(name):
print(f"Hello, {name}")
But in Haskell, I had to write:
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name)
And to even understand this, I had to dig into monads, IO, pure functions, and side effects.
After two weeks, I still couldn’t build a working project. It felt less like coding and more like studying advanced algebra.
So I gave up the first time.
I told myself: “Maybe FP just isn’t for me.”
2. Second Attempt: Elixir’s Hope and Breakdown
Six months later, I wasn’t satisfied. I wanted to give FP another chance.
This time, I chose Elixir.
Why Elixir?
- Syntax was more approachable than Haskell.
- It runs on the Erlang VM, famous for concurrency and fault tolerance.
- I heard it was used in real companies, especially for telecom and messaging.
At first, I really liked it:
defmodule Greet do
def hello(name) do
IO.puts("Hello, #{name}")
end
end
Much more straightforward than Haskell’s Monad
.
And pattern matching and the pipeline operator blew me away:
[1, 2, 3, 4]
|> Enum.map(&(&1 * 2))
|> Enum.filter(&(&1 > 4))
This means:
Take a list → multiply each element by 2 → filter numbers greater than 4.
Concise, elegant, beautiful.
But I got stuck again.
This time, on immutability.
In Python, I write this hundreds of times a day:
x = 10
x = x + 1
In Elixir, this throws an error:
x = 10
x = x + 1 # ❌ cannot rebind x
Once a variable is bound in Elixir, it can’t be changed. Every “update” creates a new binding.
This completely overturned my intuition about how programs “should” run.
After days of struggling, I gave up again.
3. Why Is Functional Programming So Hard?
I later realized:
Functional programming isn’t just different syntax — it’s a different way of thinking.
- In imperative languages (Python, Java, Go), we’re used to changing state:
Set a variable → modify it → keep modifying it in a loop.
- In functional languages, code behaves more like math functions:
Take inputs → return outputs. No sneaky global changes.
You must treat “variables” as immutable values and “processes” as pipelines of transformations.
For most programmers, this feels like rewiring your brain.
4. Three Lessons After Giving Up Twice
Even though I didn’t stick with Haskell or Elixir, I gained a lot from the experience.
1. Pure functions are powerful
- Same input → same output, every time.
- No side effects → easier testing.
2. Immutability reduces bugs
- No “hidden state changes” to trip you up.
- Code becomes more predictable.
3. You don’t need to be 100% functional
- I now use
map
,filter
, and list comprehensions in Python all the time. - That’s “borrowing” FP benefits without being locked in.
5. Voices from the Community: Why Many Abandon FP
On Reddit, Elixir vs Go sparked a fascinating discussion. Developers noted:
- Elixir/BEAM strengths: concurrency, fault tolerance (OTP), elegant syntax, Phoenix/LiveView experience.
- Elixir pain points: dynamic typing is tough for large projects (some switch to strongly typed Gleam), poor for CPU-intensive tasks, deployment trickier than Go.
- Go strengths: type safety, strong performance, mature ecosystem, single binary deployment.
- Go drawbacks: concurrency model is good, but not as batteries-included as OTP.
In short:
Elixir is for long-lived, high-concurrency systems.
Go is for tools, infrastructure, and CPU-heavy workloads.
This also explains why many people give up on FP: the learning curve is steep, and not every project needs “mathematical elegance.”
6. How to Apply FP Without Burning Out
If you want to try FP yourself, here are some tips:
1) Don’t start with Haskell
It’s too academic.
Try Scala (friendlier for Python devs), or Elixir (cleaner syntax).
If you want strong typing on the BEAM, check out Gleam.
2) Mix, don’t go extreme
Use FP principles inside your current language:
- Avoid global variables.
- Prefer returning new values instead of in-place changes.
- Use map/filter/reduce or streaming APIs.
- Isolate side effects at the boundaries (I/O, DB, network).
- Compose small functions instead of building giant ones.
3) Lower the setup barrier
Honestly, the biggest “quit point” isn’t the language — it’s the environment setup.
When I switched to tools like ServBay (a local multi-language environment manager), the experience became smoother:
- Multi-language side by side: run Python, Go, Elixir/Phoenix projects in parallel for comparison.
- Dependency isolation: separate versions and libraries, no system pollution.
- One-click start/stop: focus on learning paradigms, not fixing PATH or VM issues.
If you’re curious about FP but fear the setup overhead, this is a practical way: try FP style in your main language first, then spin up experiments in Elixir/Scala/Gleam with ServBay as your sandbox.
7. From Idea to Practice: FP in Real Projects
If you already embrace FP principles (pure functions, immutability, side-effect isolation), here’s how to apply them in production:
-
Code level
- Default to pure functions.
- Keep I/O at the edges.
- Prefer small, composable functions.
- Use immutable data structures when possible.
-
Testing
- Pure functions are easy to test.
- End-to-end tests focus on flow correctness.
-
Concurrency
- In Go: goroutines + channels + context.
- In Elixir: OTP supervision trees, process isolation, self-healing.
-
Deployment
- Go: static binary, easy delivery.
- Elixir: consider releases, node orchestration, observability.
-
Team mindset
- Reinforce “side effects at the edges” in code reviews.
- Provide FP-style code examples for onboarding.
- Start with small modules (pricing, discounts, rules).
8. Conclusion: Giving Up Doesn’t Mean Failing
I gave up on functional programming twice, but I still thank it.
Because it taught me:
- Code isn’t just about running — it’s about being maintainable.
- Mindset matters more than syntax.
- Even without becoming an FP developer, I became a better programmer.
Don’t stress about “not mastering” FP.
Even if you only learn to write pure functions, reduce side effects, and respect immutability — you’re already improving.
As for diving into Elixir/Haskell/Scala?
Start with FP style in your current language, then use ServBay to spin up local experiments. Step by step, take what’s useful and leave the rest.
Open Questions
- Have you ever tried functional programming?
- Do you think it’s worth using in real-world projects?
- Which do you prefer — Go or Elixir, and why?
Share your thoughts and stories in the comments.
This content originally appeared on DEV Community and was authored by John Still

John Still | Sciencx (2025-08-26T02:33:51+00:00) Why I Gave Up on Functional Programming — Twice — and Still Grateful for It. Retrieved from https://www.scien.cx/2025/08/26/why-i-gave-up-on-functional-programming-twice-and-still-grateful-for-it/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.