Were you a proficient programmer before that?
I have come to regard lazily evaluated infinite lists as a must-have before you can call something a functional programming language.
In trying to build more complex systems, I noticed that you really have to know what you're doing with Haskell, otherwise you end up with leaks that are very difficult to trace back.
What really solidified FP for me was taking an elective category theory for computer scientists course.
Today I'm building stuff in Scala, which is the closest you can get to being able to pay the bills while doing principled FP.
I don't write in pure FP languages now, but I do use the concepts I learned all the time, especially as their fantastic concepts are leaking into other languages, like Rust.
I don't think pure FP languages will ever be real industry winners, but I think they're great experiment and research grounds for features that the broader community eventually adopts, which is such an important thing for improving the expressibility of our code.
I didn't really grok FP until 2022, but I learned Scheme as early as 1999 for programming 101 (comp sci degree). We used the SICP textbook; looking back it seems SICP used an FP language to teach programming, but didn't explore some important FP concepts like Option/Either (a.k.a. Monads. Perhaps our class just didn't cover that part of the book?)
Between 1999 and 2022, I dabbled in FP because I heard good things about it. But I think many FP practitioners could improve their marketing/teaching skills. Most FP texts seem to dive into mathematical jargon (monads, functors, etc) without even explaining why knowing these would be useful.
So I'm writing a series of articles on FP. The first one is on why FP is worth learning: "More Performant, Testable Code with Functional Programming. (FP language optional!)"[2]
The next article will be a curated list of articles/videos that I feel explain FP better than most; the ones I wish I had found way back when starting to dabble in FP.
Also there's an argument many of FP's useful features have been copied by more imperative languages[3]. So people may be unwittingly doing FP even if they aren't using an FP language. For example, anonymous first class functions have been added to C++ and even Excel. JS has always had anonymous first class functions.
[1]: https://hw.leftium.com/#/item/18043058
[2]: https://blog.leftium.com/2023/04/more-performant-testable-co...
Nowdays, my programm is mostly the composition of some functions. Want inheritance and polymorphism ? Just use object composition and multi dispatch functions.
The way to go forward is abandon any of `class` keyword.
Protected, public, private is a lie and also a trap. It didn't prove anything.
Your encapsulation rule is inside your interfaces, not inside your class keyword.
Now you can say: But OOP served me well, i can get shit done fast !
Absolutely, any tools can serve you enough. The issue is, once you use the bad tools, you get into a rabbit hole to slow down everything else eventually.
[1] https://www.aqa.org.uk/subjects/computer-science-and-it/as-a...
Some of this stuff, you play with in "Blub" imperative languages. If you write a recursive factorial or fibonacci in Pascal, that's functional programming. Just not with higher order functions.
The C preprocessor is an example of functional calculation. (Though macros can be undefined and redefined, mainly they are just defined and called, and perform substitutions without clobbering anything.)
Unix pipelines : don't clobber anything other than the file being created or replaced at the end.
Parsing a grammar with Yacc and building a tree: basically functional. The imperative bits going on are hidden under the hood. $$ = make_node($1, $3) is an assignment, but it's boiler-plate; you don't think of it as assignment, but yielding the semantic value of the rule, which is constructed from the pattern-matched $1 and $3 pieces.
All those things teach you that it's useful and good to calculate a new value from an existing one, while leaving the original alone.
There is a lot of functional programming in the middle of imperative programming. E.g. constructing a balanced binary tree might not be functional (it can be, but often isn't in Algol-like languages), the queries on that structure are conductive to functional programming. Queries don't mutate the structure, and if recursion is used, don't mutate traversal variables.
A binary search of an array can be coded functionally via recursion.
Here is a functional version of strchr in C:
const char *strchr(const char *str, int ch)
{
return (*str == 0) ? 0
: *str == ch ? str
: strchr(str + 1, ch);
}
Lisp was something that clicked almost instantly. I came to that armed with a lot of experience and understanding, and was well-versed in recursion, plus all around systems programming as an experienced developer. The idea that, say, a tree could be recursively defined as a null value, or else a node with two children, was nothing new. Or that a recursive function could search that.
Although I don't use it in my daily work, I still tinker with Scheme and SBCL from time to time.
Over a decade later I decided to learn F# and now it makes sense.