Intro

You know, since I accidentially discovered Functional Programming due to being a Security Analyst at a time (and realizing how shitty our software practices are to allow many severe vulnerabilities into production due to C’s nonexistent memory safety), I was a hard proponent, or a more fancy word they call us, an evangelist of FP (but that is a topic of its own).

Of course, when I try to do that, I get a massive pushback from people:

  • Most of the research in my University among Master’s students done in on AI (but mostly ML/DL)
  • There are less people in my FP lab than the fingers on my hands (and 30% of them will graduate in couple of months)
  • People generate even more Python code than before (thanks to all the LLMs)

I asked myself a question every (remotely sane) person would ask at this point: “Why?”. But again, the answer is still somewhat out there. What happened, however, is that I started to look inwards in search of the answer (because Universe won’t answer my calls). What happened is that I realized that all the “5 stages™” that FP caused me, I successfully blocked out all the memories to be able to go on with my life. And now, when I am all crippled adjusted to my new life, I am pushing people into doing things they don’t like and surprised when I’m getting pushback even though it took me a whole year of forcing myself literally my every free and concious moment to internalize FP ideology (because you see, I’m a masochist curious).

So in this post we will go into my obviously objective, accurate and comprehensible account of one year that I was subjecting myself to learn FP starting from discovery until I was able to start to write my first lines of code in OCaml.

Discovery

As I said, it was purely accidential. I am one of those people who use Incognito mode to browse internet because:

  • It doesn’t clutter history with random shit that I visit
  • Drops all the unnecessary personalized cookies
  • Yields sane recommendations on YouTube (or at least used to, before I switched on DistractionFree YouTube)

So, I was sometimes recommended a video from Richard Feldman on why Functional Programming Languages™ are not popular and one time I gave in and decided to watch it. Needless to say, I didn’t understand anything of what he said the first time, because C and Python obviously have functions, duh, what are you talking about.

The next two things happened accidentially, I guess. I was in my last year of the undergraduate studies and we had to make a Senior project. In a university where most of the staff are ML-researchers and most students just want to graduate at this point and take simple web-development projects, I decided to make a world a better place… and design a compiler for a toy language that won’t be used by anyone, not even me.

I always had a fascination by compilers that I can’t explain why. On my first year of undergraduate I asked the question of how to develop a programming language (to a professor who turns out to be specializes in compilers and all the mathematical stuff). He probably thought that I was just not serious and gave me a simple answer like try researching it on your own or watch YouTube, but that I can do it if I really want it (or something of the sorts, but I remember that I didn’t get any answer). I started watching some random YouTube video on how to build my own programming language, and didn’t understand a thing. I also had a course where we had to code in MIPS assembly and most of the students struggled. I had a genius idea of developing a compiler that generates a MIPS assembly from C language, but I didn’t know how to put the idea into words and so by the time it didn’t take off the ground, the course ended (maybe I should do it on some weekend, seems like a simple project tbh). So yeah, I had some interest in compilers.

Some years into the future, I’m working as a Cyber Security Analyst, trying to make sense of what is happening for the last couple of years. And by chance I discover that there’s actually a programming language that avoids all the security pitfalls of C. “Oh wow, sounds like science-fiction!” I thought to myself. I couldn’t believe it, since if it was true, why wouldn’t people code in it more often? Turns out they do, but being good and being popular are not the same, or so it seems. And so, I tell about this fancy programming language Rust to my Senior-project teammate. “Rust? Duh, I know about it”, was kinda his reaction. He is really smart and also codes in PHP in his fulltime job (while being senior student) because it was a really good deal in terms of $/h. He is really smart. And so, I am inspired to develop a new compiler with a borrow checker for a Senior Project. Being dumb is what motivated me to make those kinds of claims. I’m kinda envious of that stupid guy. We have a saying that even an ocean is a knee-deep if you’re fool or drunk. But my professor had to fight me on that one constantly, and I retaliated back. Turns out, sometimes professors are correct. We cut out half of the stuff that I proposed (the borrow checker was the first to go) and we barely managed the project on time. If it wasn’t for our smart teammate, we wouldn’t have made it. We were coding in C++, and I was tired of a state-management in code generation. My lack of high-level view and deep understanding of what was going on didn’t help me either. So I was trying to do my best to close the gaps as soon as possible. Accidentially found out about book “Modern Compiler Implementation in ML” (far better book than “Dragon” for newbies). This was my first encounter with proper ML.

And now, with all the ideas of Rust and ML and compilers, I stumble back to the Richard Feldman’s video without realizing how all the things are interconnected. In hindsight, it is really surprising that the things that seem remote are actually interconnected: I mean security, some esoteric programming language and the art of developing compilers. Don’t you think so or are you just too smart?

And so, after the video I started to (somehow) discover that Functional Programming is (another) thing that can prevent security issues. Ain’t it perfect?! And so I start to read different blogs posts on it, trying to wrap my head around it. They all keep talking about functions (duh), about the importance of “immutability” (I’ll be honest, that’s when I first learned the meaning of the word), but also about some languages like OCaml, Haskell and Lisp. The thing is, when you’re a beginner and pretending to be smart, you’re trying to wrap your head around many things and they don’t make sense initially. Heck, they don’t make sense even after you start using them. You can make a whole academic career from from researching types alone. And so, I was lost, which brings us to the next part…

Pain and Suffering

Instead of being a normal person and forgetting all this nonsense as a bad fever dream, I started to research this in my free time. Blog posts was a mistake, so I switched to YouTube videos and books. Turns out there aren’t as many books on FP as on, say, Python. And videos also differ their content, length and quality. But the thing is that the materials are scarce, so when you start searching for them, you’ll start seeing the same videos and books over and over again (unless you go onto some real books which are pre-2000). And so, you’re forced to work with what you’re given and try to actually listen and understand what people are saying. The first thing that stands out, is that you can’t actually dumb your way through FP, like you did in, say, Python. You actually have to think about what is being told, what is not told, what are the implications of what being said and most importantly, how to put it into practice. Math people are good at this, but they tend to overestimate other people’s capabilities and make a grave mistake thinking that we understand them.

I really liked NDC Conference videos on FP. Surprisingly, they have a lot of them and they are actually really good. I liked Scott Wlaschin’s videos on FP and F# (but mostly FP). 13 ways to program a turle, domain-driven development with F# and railway-oriented programming, those are nice things. But even when the “railway-oriented” sounds way nicer than “monads”, it still took me some time to understand what is being said (and I guess some initial practice was crucial in that). Actually, because of Scott Wlaschin (and because I couldn’t figure out how to install and run other tools) I decided to go with F#.

The problem with FP is that you can basically do FP in anything, even in assembly (with much effort and dedication). In OOP it is also kinda the same, but when you learn one language, you basically learn them all, and they start to look the same to you. FP languages are also kinda the same, but to see that, you need to learn lambda-calculus. You see, the bar is kinda high in this case.

When you’re given all the choices among the different languages like Lisp, Haskell, OCaml, F#, Erlang, Elixir, Scala and just so many more that I’m lazy to list, as a newbie you just REALLY start wondering where you should start. Oh, and there’s like Common Lisp and then there’s Scheme (and there are constant sophisticated debates on which is better which don’t really answer the question with which it is better to begin). Oh, and then there are like 20+ dialects, compilers and interpreters of Common Lisp (don’t quote me on that, I’m too lazy to count the variants of CL-compliant Lisps). Oh, there’s also OCaml and Standard-ML (among which there are many different versions by different universities, but don’t worry, you won’t code in any of them :D) Erlang, Elixir, quite self-explanatory go with Elixir. Haskell is kinda standalone, but then there’s PureScript and around 2000-pages book on learning FP along with PureScript. F# is basically an OCaml for .NET. Scala is said to be nice language, but then again, I can’t say it is a pure FP. I’m sorry, I detest objects and try to avoid them (even in OCaml).

So yeah, all those languages are kinda the same, but when you’re just a beginner, it doesn’t seem so and the choices seem overwhelming and pundit and pretentious discussions on HN don’t really help newbies. So because of Scott Wlaschin I decided to go with F#. Man, it was a mistake. By a pure chance I managed to install the SDK and randomly setup *.fsproj. But I wanted to learn FP, not .NET, goddamnit. And that’s kinda another problem with all the FP languages: they work by some internal logic that developers came up with, and will work in a specific way only and you need to be aware of that or fuck off read some (nonexistant) documentation. FP really has made it so only smart people can successfully navigate it, huh? Needless to say, I never went back to F#.

Scott Wlaschin is great when you have some basics and know what you’re looking for. But as a beginner, what had a great impact on me is the video on “Approaching problems as a Functional Programmer” or something. While I didn’t understand the first half, the second half was a demo on how to change the mess of JavaScript code into something beautiful. What he did was actually quite simple, he made all the functions pure by repeating the same logic over and over. But when you’re seeing it for the first time, it felt like witnessing a magic trick: you like it and can’t get enough. It also fuels your motivation to learn the secret. And so I decided to repeat the experience.

I didn’t know JavaScript good enough, so I decided to go with Python. At this point I discovered Coconut, a programming language that is superset of Python and is functional. I liked the idea. Except it was a bad idea. The main thing that holds Coconut back is documentation and development tools. Again, the same problem, I was trying to learn FP, not Coconut. And so, I had to bid Coconut a goodbye, but it was a (somewhat) pleasing experience for a first time since trying to learn FP. Then there was Hy, but I guess I don’t like brackets, I’m sorry.

And so, no more excuses. Me, Python and FP. So, what do I do now?

I started small. Not because I was afraid, but because I couldn’t do much. I was still dependent on mutation, local state and for-loops instead of functional counterparts. Somehow, the most difficult part was to make that shift in paradigm, to approach problems from a new angle. When you look back, you realize it wasn’t so difficult. But it was hard. It was simple, but it wasn’t easy. That is a subtle, but important difference. Additionally, when you come back to Python, you’ll notice that many modules actually encourage mutating and imperative styles, sometimes it is the ONLY way to do things. Surprisingly, I liked pyplot a lot, because it was declarative and it made sense after all the FP and logic background.

Slowly, I started to make more and more in functional style. I started to make state explicit via input arguments. I started making more functions for each small thing (my friends still say that I’m overkilling it). I started to use recursion, pattern matching, higher-order functions, lambdas and list-comprehensions where I could. I started to decompose problems into smaller, easier problems and started to make my code more modular and more testable. One of the joys was figuring out that I can use Python’s built-in lists as a configuration DSL (and get code-highlight and formatting for free!). But the best thing, of course, is that the idea worked. It matched the problem I was trying to solve so naturally, I couldn’t believe it. I wanted to share my joy with others and started to become a proponent of Functional Programming (much to annoyance of my colleagues and friends) without actually understanding what it is.

Of course, trying it out in Python helped me to deepen and practice lessons learned from FP. Also, the book “Grokking Functional Programming” is a real godsend. Maybe we should recommend it to anyone starting out in FP?

At some point however, I started be constrained by the language. Needless to say, Python is not suited for Functional Programming. You can do it, but with much effort, and when you do, you get unpythonic code. You also get punished by slow performance and heavy memory usage. Oh, and there are unintended side-effects like unclear scopes, variable shadowing and passing references instead of copies. Oh, and Guido was against lambdas initially. To the point that people started to hack-in lambdas with macros and whatnot. Man, I learned more about Python while trying to learn FP than in all the years prior. I started to hate Python. I now see that all the mistakes that I make and that prevented me from seeing the functional paradigm, it was because of deliberate design choices of Python. Python must burn, in my opinion, somewhere deep in hell. But most importantly, it had to go.

Which brings me to the next topic…

Crossing the bridge

At this point I was dismayed. I wanted to do FP. I saw the potential. But I was held back by my lack of skill. The only languages I knew where C++ and Python, and I knew Python better than C++. And I also was still a Cyber Security Analyst: solving real problems comes first, FP and abstract problems second. I was watching all the videos on YouTube, of people giving talks about how they use those FP languages and neatly solve all their problems. I wanted to do the same, just like them. And so I was searching for ways to integrate those languages at my job. Nobody was in support of that. Looking back, I see why (I also see that if we did, it may be a better thing in a long term, but again, another topic for another day). Needless to say, it was disheartening. But I was still trying. I was trying to give presentations on FP stuff that I recently learned in hopes to: 1) teach some of it to my colleagues, 2) better understand it myself. I once made a grave mistake of trying to explain functors and monads to colleagues. But it was fun seeing their attention slowly drifting away. Kinda.

In the end, I knew that I had to do it myself. I have to be the proponent of change that I want to see. I must learn those languages myself and code in them myself. But to do that, I need to decide on the language to learn. And so back to the drawing board. With my “aquired taste” for FP I could better appreciate all the benefits of different FP languages and they were asking in return (not really, but it was nice to have less fog in your head, you know?). Still, I couldn’t decide. And so I made an obvious decision: why not learn them all?

And so, I was now reading many books about different languages. It is a funny experience in itself. You start to notice many ways in which the languages are similar, but then the quirks and design choices start to become more apparent.

But what happened next was even more surprising: languages started to fall off on their own.

  • Scala was first to go (partially to some resemblence to Python syntax, but mainly OOP). I was trying to learn FP.
  • Elixir and Erlang: I didn’t want to go through dynamic typing again.
  • Lisp: brackets were too much.
  • F#: I tried toying with it a bit, but I still couldn’t become friends with .NET

The choices were shrinking, which is good, but also scary: what if I won’t like any of the languages left? There are mainly two options here: Haskell and OCaml.

Haskell is regarded as a big daddy of Functional Programming, which I think is sad. Haskell has many design choices that require experience and deliberate understanding to appreciate them, and even then not all of the choices are good. But most importantly is that it’s hard for beginners. I thought it was a meme that “Hello World” in Haskell comes in the last 20% of Haskell books, until I saw it myself. I think people who like Haskell can’t really write books: most of them are about list manipulations and functors. Writing a beginner-friendly book about Haskell is one of my dream goals.

And so, I decided to try OCaml. Before I started to look into it, I thought that OCaml was a dead language: it has even less lines of code than Haskell, which I thought was also a useless language that no-one considers seriously. I thought that OCaml has a bad ecosystem (which it does, compared to JS/Python/Haskell). I couldn’t install the SDK properly (opam is opionated and won’t work in other terminal sessions unless you do eval $(opam env)). Also, I didn’t know how to compile and build projects in OCaml. I literally had to ask ChatGPT how to do that, and thankfully it helped with dune and ocamlbuild. I like ocamlbuild more, but its documentation could be WAY better. dune I really don’t like. Maybe I’ll write my own build system, who knows.

I didn’t like the fact that writing OCaml without LSP is really difficult: all the high-load goes onto developer instead of the computer. Turns out, this is something that you have to get used to (or install VSCode (which I also don’t like), and use Dune, (which I like even less), if you want to get that nice LSP support). Editor support could be way better. As a compromise, I’ve settled with Emacs for now, although I’m using doom-emacs, mainly because of vim-bindings (this text was written in vanilla vim btw).

Writing code in OCaml is a challenge in its own. The first thing that was throwing me off (or ocamlbuild) is that you need in for every local let binding. I didn’t understand why, since in F# I didn’t need to do that. At first it was a drag, although with time I started to appreciate that aspect of OCaml and wouldn’t want to have it any other way now. Another problem is that you often don’t know what to write and how to write. Writing in OCaml is kinda like art. If you don’t know what you’re writing, then what you’re writing is bad, and it shows really quickly. I’m currently solving 99 problems in OCaml. While I think that my code is nice, there is some guy that has way more succinct code. It is eye-opening to see many ways in which one can write OCaml.

And this is another problem: all of this sounds easy, but it actually took me a long time to learn about OCaml before I could start writing my first lines of code. I literally finished “Real World OCaml” from beginning to end to see all there is to see about the language. What was nice about the Python is that with some minimal knowledge you can start writing some useful code or be able to read parts of others’ codes. It’s not the case with OCaml. For me I had to see all the parts of OCaml and how they fit together. It took me a whole book to realize that OCaml is just a lambda-calculus with ergonomics. But then there’s a module system. Module system is used to solve problems you never knew existed. Why the hell do I need to write

module IntMap = Map.Make(module struct key = int let compare = compare end)

when in F# I can write

type IntMap = Map<int, string>

And that there’s a difference between module Foo and module type Foo and I couldn’t understand why compiler is unhappy. And why every file name must be unique and how actually structure the project. And why you don’t actually need to import modules that you want to use. You’re actually interacting with module system right away and it shapes the way you approach OCaml, in one way or another.

What I don’t like about all the FP stuff, is that you have all this upfront cost that you need to pay before you can actually start learning and do something useful. I think it can be improved in many ways. Our ecosystem could be way better. And then there are misconceptions about our community. But some of them are on point. I mean, you’re thinking whether you should learn Haskell and then some guy shows off how he implemented factorial and fibonacci sequence in Haskell type system. I mean, do we really need to scare newbies?

But then again, there’s also a OCaml’s younger, but much more difficult brother - Coq. I’ll get to him someday. Eventually.

Conclusion

There aren’t many conclusions to make, other than that learning FP is hard by design. Some obstacles are artificial, while others are inherent. What I showed you was just my way of how I got here.

The sad part about this all is that I can’t walk this path twice. Despite my gruelling and whinning, this experience was also really fun. It was a joy to discover new things and understand how world operates on a different level. I somehow stuck through the difficult storms and now I’m on the outskirts of the the world, in the barren lands where I’m free to build my kingdom or help others and few survivors who happen to share the similar mindset as me. This is nice in its own way.

I also learned that sometimes the only way to do difficult stuff is by just doing it. Sometimes there’s no other way.

If you’re reading this in hopes of learning FP or deciding if you should learn it: good luck. It will be worth it.