Why is Learning Functional Programming So Damned Hard?

Charles Scalfani
21 min readNov 30, 2019

My Functional Programming journey was filled with dead ends, false starts, failed attempts and frustration. And I suspect that I’m not alone in this struggle. So why is this a common problem and what can be done about it? And how can you avoid the these same pitfalls?

TLDR: My solution circa 2021, Functional Programming Made Easier: A Step-by-Step Guide.

Learning to Program

My initial programming journey couldn’t have been more different than my functional one. I started back before anyone had personal computers. I started by programming my Texas Instruments calculator and eventually got my own computer that saved programs on a cassette tape.

I was lucky, because I got to learn and grow up with the industry. I learned FORTRAN, Basic, COBOL, 6502 Assembler, and Pascal as a student.

Once I got my first programming job at 19, I learned Z80 assembly and then C and later C++. In those days, there was no ecosystem of free libraries to download. You had to write everything yourself. And so you were never expected to produce anywhere near what we expect developers to produce today.

After a decade, there was Java, .NET languages and JavaScript. There were many other languages along the way, but these are the milestone ones.

Starting programming when I did allowed me to SLOWLY learn new concepts. But more importantly, each language built on knowledge I had already developed from previous languages. Admittedly, the first time I cracked open the first edition of C Programming Language book, I swore I was looking at hieroglyphics. But it was more of a syntactical problem. The concepts weren’t that different than FORTRAN, Basic and Pascal.

The learning process was easy and I now realize, unusual. Most people who enter the programming field, weren’t born in the 60s and instead enter into a fully formed industry. I never appreciated how good I had it until two significant events.

The first was teaching my daughter programming. When I sat down to plan the process, I realized not only how much I knew, but more importantly, how much she needed to know in order to get a job as a productive developer.

When I started programming, I might be expected to write some Z80 assembly code to print characters to a printer. When she started at her first intern job, she was expected to write a server in Node to monitor websites.

The second event that really opened my eyes to how difficult learning programming can be was when I tried to learn functional programming. For the first time in over 30 years, I was a NOOB.

We’re Not in Kansas Anymore

One Christmas, when I was a kid, my parents were planning to buy me an HP 41CV calculator. It was around the time I had started learning programming on my college’s Univac Mainframe. So to encourage me, or more likely to discourage my current career plans of becoming a Theoretical Physicist, they found a used computer that was only $500 more than the calculator.

They gave me a choice. The calculator or the computer, but I’d have to pay them the $500 difference. I wasn’t sure so we took a quick trip to the local computer store.

I was flanked by each parent as the salesman spoke words I could only pronounce: RAM, ROM, ASCII, 8K, bytes, Basic, monitor, processor, etc. As I listened, struggling to hide my confusion, my parents watched us like it was a tennis match. First to the salesman, just long enough to be overwhelmed and then back to me to see if I was following. And then back to the salesman. Back and forth as the onslaught of jargon bounced squarely off my forehead.

When the salesman excused himself to help a customer at the register, my parents asked me, “Did you understand what he said?” I paused for a moment and said, “I didn’t understand a single word… I want the computer.”

They bought me the computer that night. When we got home, I set it up on a TV tray and began to slowly and deliberately work through the scant manual. I had never seen a computer before and while I wasn’t afraid of it, I didn’t want to make any mistakes.

Once I figured out how to hook the monitor up and turn it on, the very first thing I ever did with that machine was to add two numbers. I typed directly and carefully from the manual:

PRINT 5 + 2

And it displayed the number seven. I was speechless for just a moment and then yelled to my mother who was cooking in the kitchen, “Hey, mom! You’re not going to believe this. This thing isn’t just a computer. It’s also a calculator!”

We’re Not in Kansas Anymore — The Sequel

Fast forward 30 plus years. As one would expect, I’m not living at home with my parents anymore. I have a family and responsibilities. My job is very demanding as the CTO of a small company.

Unlike younger me, I couldn’t spend 10 hours a day playing/learning in the Computer Lab on campus. I had real world demands. People depended on me both at work and at home. And after a full day of work, I’d given the majority of my brain power to my job. What little was left was mostly consumed with family life.

It was in this climate that I had a very similar experience as I did that day at the computer store. I had been reading about this thing called Functional Programming. And when I researched it, I found that Haskell was the de facto standard in that arena.

So, I naively decided to learn yet another language. While I didn’t have too much time or energy after work, I did have time during the weekends and so decided to see what this Haskell thing was all about.

How difficult could it be? At this stage of my career, I’ve learned literally dozens and dozens of languages. Each one was easier to learn than the last. Usually, each new language would add, maybe, one or two new ideas. I expected this to go pretty quickly.

How wrong I was.

Just like during the salesman’s pitch, I hit a wall of jargon, Monoid, Functor, Currying, Lambda, Purity, Side-Effects, Monad, etc. But unlike that first jargon gauntlet, my reaction was very different.

I recoiled. I deflected. I blamed those academic eggheads and their big fancy words whose only purpose was to exclude and prove their mental superiority.

Consolation Prize

I don’t usually give up easily. And it bothered me that after over 30 years in the business, I failed to learn a lousy programming language. So from time to time, I’d get motivated and take another swing at it. And just like before, I’d bounce off the wall of jargon. Searching the Internet didn’t help, because it seems that all of the people who understood Haskell, were infected with Jargon-itis.

So I settled on learning about Functional JavaScript since it was possible to do some functional things, e.g. JavaScript has Functions as First-Class Objects. So I dabbled with Currying even though I didn’t understand why anyone would want to do such a thing.

I tried to write my own functional JavaScript library, mainly for learning. And then I found Ramda, a functional JavaScript library that was far better than the one I was working on.

To fully understand what it feels like to use JavaScript like a functional language, you have to imagine using the heel of your shoe to hammer a nail. Yes, the nail is slowly and painfully driven into the wood, but neither the nail nor shoe are better off for it.

Kindergarten

I taught the programmers at work some of the simpler functional ideas and how to use Ramda. And for awhile, we all wrote JavaScript like we had always done before, but now we occasionally seasoned it with a bit of functional code.

Not long after that while we were doing our technological research for a new project, we bumped into Elm. And when we did, it was a breath of fresh air. Elm wasn’t laden with jargon. Sure it looked like Haskell, but I didn’t need a PhD to understand it.

I took to Elm like a drowning man takes to a life-preserver. It was saving me from learning Haskell and all of its hard concepts.

What Elm taught me was how to think Purely, i.e. you cannot mutate any of your variables. It taught me how to code more functionally. It reminded me of the huge benefits of Static Types without the clunkiness of Types like in Java.

I thought I’d program in Elm for the rest of my career or at least a large part of what’s left. I even created an open-source tool to help in writing Elm on the server, something the language was not created for.

Our next project was up to over 30,000 lines of Elm code when we hit a wall of a different sort.

Don’t Heckle the Man with the Mike

My really cool open-source tool got some attention. But not quite the attention I had hoped.

My goal was to help others do what we were doing, i.e. write server-side Elm. I wanted to support Elm and help it succeed. And this tool was only a part of that intent.

Unfortunately, the language creator was none too happy that I was making it easy, and hence, encouraging people, to use his language in ways, as he says, it was never intended to be used.

He tried to explain to me that there are far better languages, e.g. Haskell and PureScript, to do the type of things we were trying to do with Elm (for the record he was right, there are far better languages than Elm). But I didn’t want those other languages. I wanted an easy language. And I already knew Elm and it was easy.

He informed me that he was planning to take away our ability to do what we were doing at the compiler level in the very next release. To say the least, I wasn’t happy. To say the most, I was pissed off.

Another programmer at our company wanted to fork the compiler and take out the restrictions. Now I’m a rebel at heart and while this sounded good in theory and it was fun to fantasize, I knew better than to heckle the man with microphone. You always lose.

College

So over 30,000 lines of code went in the bit bucket.

Now what?

I could NOT go back to writing servers in JavaScript using Node. Not after Elm. I was spoiled. I had seen the huge reduction in bugs, enjoyed the speed and ease of reading and writing functional code and cherished the ability to refactor effortlessly with confidence. I never wanted to go back.

We could still write our front end code in Elm. Unfortunately, for technical reasons, we couldn’t use any of our front end code from our 30,000 lines of code.

But for the server, I had no other choice but to graduate to Haskell.

At this point, I had tried to learn Haskell on 3 or 4 separate occasions. All failures. And going from Elm to Haskell is like going from Kindergarten to College. So what made me think I could do it this time?

This time, I had no choice. I had a real reason to learn it. And that was the game changer.

Motivation will make us endure nearly anything and for the next 3 months, I lived and breathed learning Haskell. It was my work and my hobby. It was the hardest thing I’ve had to do in my career.

It was like wallowing in the mud all day. Just flailing about wildly with no real direction. Going from book to book. Blog post to blog post. And by the end of the day, I’d rise from my mud patch and notice hardly a bit of it stuck to my skin.

The next day, I’d repeat the process, wallowing and flailing. And by day’s end, there was only a minuscule amount of mud on me. It felt like none of it was sticking.

This went on for weeks and ultimately 3 months. I’d start the day with only a few tabs open in my browser and by days end I’d have 20 or 30 as I plummeted down the rabbit hole. I likened it to learning German with only a German dictionary.

I was living the nightmare every modern, programmer-to-be knows all too well. An experience that I didn’t understand because I grew up in a different time. There’s just too damn much to know and you don’t know enough to know how to prioritize your learning.

The problem is that everyone has a different opinion on what’s important and when you read comments on the Internet, you have no way to know who to believe.

Many advanced users give advice to noobs like me as if we were advanced. Such advice taken to heart wasted countless hours of being frustrated because I was in over my head and I didn’t know it.

Books, articles, wikis and blog posts are all at varying levels of advancement. Most of them are off limits when you start. Beginner books are too beginner. You can get through them and be under the delusion that you understand. Then you start to read someone else’s code and find yourself completely lost.

But even with all of that, I came out of those painful 3 months a Haskell programmer. Well, a beginner Haskell programmer. I still didn’t quite understand Monads. But I could use them. And for what I needed at the time, that was good enough.

Good Enough

Now that I could write server code in Haskell and we had Elm for the front end, we were off to the races to develop our product. But not before I trained our JavaScript developers in Elm.

Having been through the pain of going from Elm, an environment that prides itself as being jargon-free, to Haskell, the proverbial primordial soup of jargon, I didn’t shy away from using the words, Functor, Applicative and Monads in my class.

I did this so that they would get numb to these words. You can certainly learn Elm without ever hearing these words. I did. But if my students have to learn Haskell or PureScript, I didn’t want these words to induce the same level of fear as they did in me. And it’s a good thing since we’re moving to PureScript on the front end of our next project.

After a long arduous journey and a lot of blood, sweat and tears, we now have a product that’s built using Pure Functional Languages on both the front and backends. It wasn’t easy, but it was worth it.

Why is it So Damned Hard, Already?

There is no single reason why learning Functional Programming is difficult, but here are a few of the biggies (your mileage may vary).

Old Dogs

For people like me, i.e. someone who has been successfully programming for a long time in Imperative Languages, it’s especially difficult because you have to unlearn many things. It’s almost like you’re learning a completely different skill from scratch.

Most professionals fall into this category. Many have the added burden of thinking in an Object Oriented model. I wasn’t burdened with this as I had given up on OO over 25 years ago. (See Goodbye Object Oriented Programming)

It’s probably far easier for new programmers to learn Functional Programming than it is to teach the “old dogs”.

School’s Out

Another major problem is the Educational System. A great example of this is a Monoid. This concept is from Abstract Algebra along with another useful concept called a Semigroup.

These concepts sound hard but are actually quite simple. Programmers deal with Monoids nearly everyday they code. They just don’t know it. I could probably walk into any 7th grade Algebra class and teach them this concept in a single lecture.

I learned these concepts over 40 years after my 7th grade Algebra class. And I’m no slacker when it comes to math. In college, I got As and Bs in 4 semesters of Calculus and a semester of Linear Algebra and Differential Equations.

But those are the old maths. Well, all math is old, but these math classes are for an Industrial Revolution world not the Information World we all live in today. We should be teaching a much different set of maths.

Also the Educational System suffers from Bipolar Disorder. They either teach only theoretics such that graduates cannot write a single line of code. Or they teach people to only program. And they do this in languages whose heydays are behind them because the industry has to maintain millions of lines of code in those languages.

This is short-sighted and slows the advancement of the industry.

Expert, texpert choking smokers

Too many people on the Internet pose as experts. Most mean well. But the result is that learning programming on the Internet, especially functional programming, is made far more difficult due to those who think they can teach but cannot.

I learned very quickly how bad my teaching was when I started to teach my daughter programming. I learned how to teach while I taught her programming.

Many people think they understand something right up until the point they have to explain or teach it to someone else. That’s what separates the wheat from the chaff. And far too many who teach are chaff.

A perfect example of this phenomena is seen in the proverbial Monad tutorial. The joke is that as soon as someone learns what a Monad is, they experience 2 things. One, the uncontrollable urge to write a Monad tutorial and two, they lose any ability to explain what a Monad is to anybody, even to those who already understand it.

I’ve read many of them and most are terrible. And not to be outdone, I even wrote my own, How to think about Monads. Hopefully, mine is less terrible than most, but perhaps not.

Another expert problem is that Haskell is a language for both professional development and for language researchers. It’s very difficult for a noob to distinguish the subjects that are bread-and-butter Haskell and those which are esoteric or experimental.

Book ’em, Danno!

Books used to be the go to technology for learning. As an old guy, I must say I miss those days. Sort of. There’s a huge benefit from being able to write something like this article or a blog post and have it published for free instantly.

That allows lots of people to benefit from your knowledge or experience. And unlike books, the barrier to entry is nearly zero. And that is also their biggest downside.

Typically, books are much better written, edited and they are usually structured. More time is spent on them and there’s a sense of permanence. So, people typically take far more care creating them.

With a book, you can’t just open a browser window and just start typing. Well, technically, you could. I read one during my 3 months of emersion that was over 1000 pages and could’ve been easily 300 pages if they had a good editor who was also a functional programmer (also known as a unicorn).

Patience, Young Grasshopper

I had only 3 months to learn enough Haskell to write a fairly sophisticated server. This is hardly the ideal situation. As we all know, time pressure makes learning very difficult.

Don’t wait to learn these concepts. Learn them slowly over years. This is becoming easier and easier since other languages are slowly adopting some functional concepts.

Another thing to do is learn in the right order.

Today, if I were trying to learn Functional Programming from scratch, I would do it in this order:

  1. Functional Concepts, e.g. purity, composition, currying, immutability, etc. (as a start, see my 6 part series, So You Want to be a Functional Programmer)
  2. Elm
  3. PureScript
  4. Haskell

I always say that Elm is like Kindergarten, PureScript is like High School and Haskell is like College.

Remember how I went from Kindergarten to College. Not easy. And not a great idea. Far better to learn in increasing order of complexity.

I can say that learning PureScript has made me a better Haskell programmer. Imagine how much easier it would have been if I would have learned in an increasing order.

Having said all of this does not preclude jumping right into Haskell or PureScript. But I wouldn’t suggest this for anyone except for brand new programmers. Once your brain has been infected with Imperative Programming for 2 to 3 years, it’s takes a huge amount of energy to unlearn before you learn. The longer you’ve been doing it, the more effort it takes.

Possible Solutions

I said Haskell was the hardest thing I had to learn in my career. More accurately, learning Functional Programming concepts used in Haskell in 3 months after having thrown out 30,000 lines of code on a project that was now monumentally behind schedule was the hardest thing I had to do in my career.

Learning Functional Programming doesn’t have to be hard. Having a curated list of resources ordered by advancement would be helpful. We also need better tutorials and learning materials.

Most tutorials are written by two types of people, the expert or the novice. The expert talks over the heads of nearly everyone because they’re interested in the subject matter at a more advanced level. They forget what it’s like to not understand something.

Then there is the novice who is excited to have just learned a subject and they want to tell the world. Their tutorials are too often poorly conceived and incomplete and usually fraught with errors.

However, if I had to pick my poison, I would go with the novice since I can at least understand what they’re talking about. But ideally, working developers who have been working with these concepts for a few years would be the teachers. They are advanced enough to understand the subject well, but not so advanced they have forgotten how hard it was to learn in the first place.

When I was teaching my daughter programming, she asked me a very simple question: why do we use functions? This seemingly simple question stopped me cold. I had been using functions for so long, I really never gave it much thought. But worst of all, I couldn’t give her a very good answer. It took me far too long to come up with a simple reason. And by then, she had figured it out for herself and even later marveled at the naïveté of the original question.

The reason I failed so miserably was because I suffered from what most people do when trying to teach Functional Programmers. I was experienced. (see Why Experts Make Bad Teachers)

Is This Really Worth It?

Is learning Functional Programming worth all of this trouble?

To answer that, we must consider two things, cost of acquisition and cost of use.

Acquisition Cost

This is where Functional Programming is expensive. It can take a long time to learn, e.g. learning Haskell takes approximately 3 to 5 times longer than learning Javascript .

To understand this, imagine two people decide to learn to program. One picks Haskell as their first language while the other picks Javascript.

The Haskell programmer has to choose between multiple editors and build tools. This step is extremely difficult since they have no idea how to discriminate. First they have to download an editor and learn to use it. Next, they have to download their build tools and learn to use them.

The Haskell programmer can write the canonical “Hello, world” program, but they won’t fully understand what they’ve written for months. I’ve joked that when I learned C, the first thing I learned was “Hello, world” and when I learned Haskell, it was the last.

Contrasting this with the Javascript programmer’s experience. All they have to do is open their browser. Then they have to learn how to access their browser’s console. And finally, they type in the “Hello, world” program. They can understand all of the code they wrote in a matter of minutes and at most hours.

The barrier to entry to Javascript is nearly zero. Nearly, everyone has access to Javascript since it’s available inside every browser. If you write a simple Javascript program, you can learn to code without having to learn any difficult concepts. Eventually, you’ll have to install an editor, but it’s not necessary to get started.

Haskell, on the other hand, has a much higher barrier to entry. You must first install the compiler and then an editor. You are now ready to type your first program, but you’re required to write code that you won’t understand for months to simply output the results of your code.

Haskell requires you to learn concepts from Abstract Algebra to Category Theory. Javascript has simple to learn mechanisms whereas Haskell’s mechanisms, while far more powerful, are also far more difficult to learn. This means that a Javascript programmer is productive 5 to 10 times faster than their Haskell counterpart.

Cost of Use

This is where Javascript and Haskell swap places.

The cost of programming in Javascript is a price you pay daily. It super easy to create bugs that you don’t find until it’s too late, many times after the code is in production.

The Technological Debt, i.e. the cost and burden of a large codebase that’s complex and difficult to maintain, is also higher in Javascript. One of the reasons for this is that the language is fragile.

Once the heavy upfront costs have been paid, Haskell starts to pay dividends. It’s still possible to write bad Haskell, but the quality of code you can write with Haskell can never be achieved with Javascript.

The code size is also much smaller with Haskell. That’s because of it’s powerful abstraction mechanisms. It was these mechanisms that made our learning so difficult. With all of that behind us now, we are able to leverage these powerful abstractions to write a few lines of code to get a lot more work done.

I always joke that in Javascript you think for 2 minutes and code for 2 hours. But in Haskell you think for 2 hours and code for 2 minutes.

The price paid to learn Javascript was super low, but the daily usage cost, depending on codebase size, can be astronomical.

Haskell is very difficult to learn at first, but the daily cost is so much smaller that it doesn’t take very long for the total cost of programming in Javascript to overtake the total cost of Haskell. And, differences grow larger the longer you program.

Investing

Programming in a Functional Programming Language is a long-term investment where most of the cost is upfront. This means that you have to take the stories you hear about the benefits on faith since you won’t experience the benefits firsthand for many months.

Imperative Languages, like Javascript, are a short-sighted investment. At first, the choice to use Javascript feels like you’ve made the right decision because while your Haskell counterparts are still trying to learn some obscure concept from Abstract Algebra, you and your colleagues are getting real work done.

But programming isn’t about writing code once and forgetting about it. We programmers are always changing the code to fix it, expand it’s functionality, make it more efficient, etc. By it’s nature, it’s a long-term commitment.

So, is learning Functional Programming worth it?

When you do the math, Functional Programming costs less to develop and pays dividends every time you have to change the code. You also have a more highly skilled team than you would with the imperative counterparts. If these are valuable to you and your business then it’s more than worth it.

Conclusion

I’ve seen a lot of technological changes come and go and am usually ahead of the technology curve. (Except for the Internet. Somehow I missed that one.)

I believe that Functional Programming is far far better than Imperative Programming. I know this because I was willing to suffer for 3 straight months learning something I had failed at multiple times in the past just so I didn’t have to write in JavaScript.

There are great resources out there even if they’re buried in a mass of mediocre material. Hopefully, Functional Programming Languages will gain enough popularity that developers with the right amount of experience will be motivated to contribute to the ecosystem of educational material.

In the meantime, we are destined to scour the Internet to cobble together our own curriculums from wikis, blog and forum posts, academic papers, books, etc. But even with all of the odds stacked against us, I think it’s still worth it.

I believe every single programmer would benefit from learning Functional Programing and if you have more than 10 years left in your programming career, you’re going to have to learn them eventually.

You can either learn them on your schedule or on someone else’s. Trust me, someone else’s is NOT fun. Not fun at all.

--

--

Charles Scalfani

Software Engineer and Architect, Teacher, Writer, Filmmaker, Photographer, Artist…