An excellent list. Regarding functional programming, I recommend starting with a gentle approach that doesn't require picking up a new language:
1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
2. Take the time to really understand what side effects are, and start avoiding them everywhere they are not necessary. Keep scopes as local as is practical.
3. When you start toying with functional programming per se, make sure you really have your head around recursion. That's where much of the magic concision comes from.
Agreed on gentle approach. I've started using libraries like Ramda for JS, which is especially designed to ease people into functional patterns.
I then moved to FP-TS, which makes more heavy use of haskell-like patterns and monads.
The hard part isn't the syntax of whatever language, but understanding the new patterns and way of thinking, which you can do with a simulation layer like FP-TS (for typescript/javascript).
The functional patterns and emphasis on types makes your code robust and more correct. It emphasizes correctness, which is ultimately what your job is as a programmer. Optimization comes after.
There could be other reasons for disliking it other than not understanding what’s going on. Maybe other people are working on tight deadlines and don’t have the time or mental energy trying to understand an entirely unfamiliar programming paradigm.
I would want to program in functional too, but I would seek out projects or teams that already use functional. I’d never introduce a functional language to an already established team or project, unless of course, I was the CTO and there were clear benefits.
If you practice domain driven design, you can always start out isolated projects or even just new modules if your code is sufficiently modularized. I started out by making a new library/module with Ramda, and then it's consumed via a function call that anyone programming in any style can use.
Agreed that functional patterns can be hard to understand if you don't know the patterns, but if you do know them they are much easier to understand and reason about the code. It's a long term investment, and one that frankly I believe will be inevitable as more and more people start doing programming work.
Had the same result, and had to settle for the middle ground of (lodash | underscore; I don't recall which now.)
There's a lot of resistance to adopting anything with the name functional, and that resistance is often seen in a /refusal/ to try to understand instead of a mere lack of understanding: people put up walls straightaway. I expect many need motivating examples to guide them to it.
Languages like Kotlin are pointing the way towards that middle ground far more effectively than, say, Scala did.
> I recommend starting with a gentle approach that doesn't require picking up a new language
Disagree. This is likely to 'dilute' the lessons of functional programming, as it were. If you learn to program in idiomatic Clojure/OCaml/Haskell/Scheme, you can be relatively sure you really have picked up the principles of functional programming.
If you merely attempt to apply new principles in an environment you're already comfortable in, things aren't going to 'click' in the same way.
Beside that, plenty of languages simply lack the facilities needed to effectively use the concepts of FP, as vmchale says.
I can confirm this. Although I knew the principles of FP, concepts didn't click until I started using a functional language.
In non-FP languages, I didn't originally appreciate the benefits of the pattern. It was more work to do things functionally, so I dismissed some patterns that were actually useful.
I'm a bit biased, but would recommend Elixir as an accessible FP language. It has an accessible syntax and modern tooling.
You may be frustrated with the constraints of immutability for a few weeks, but the benefits become apparent once you're used to it.
Now when I work in non-FP languages like JavaScript, I will apply FP principles when it makes sense.
Some people say learning Latin makes you a better writer, smarter, etc. even if you're unlikely to directly use it. Dubious claims but it feels like FP can be like that.
The thing about learning Latin (not that I am great at it), at least for an English native speaker, and the epiphany English language speakers have, is the realization that language can have structure and can be discover-able, if I know a root word then I can almost be sure that I know the meaning of con, re, dom etc. There is just no equivalent to that in English other than the Latin origin words we borrowed, because we just assimilate any words we like and make up new ones as we see fit. An example would be beaucoup a Vietnamese word but most US English speakers would know it means big. Without the historical reference of a movie you would not have a reason or rhyme why an Asian origin word made it into English. There is literally no reason or rhyme to most of the spoken language and I think that is the epiphany, that some people actually thought out a logical way to create a language and via that logic it is discover-able.
Very cool. Did not know this. Will have to tell my Vietnamese wife. We were trying to figure out if English had any Vietnamese words. I think this counts.
Sure. When learning Latin, well, you learn to read and write Latin. You absorb the language's principles by learning the language, not by trying to highlight them in a language you already know. That can only give a much shallower appreciation.
Not quite true - many complexities of grammar are shared between the languages and it is often useful to structure the learning of Latin around the English patterns of grammar.
I imagine that diligently learning a foreign language (dead or otherwise) will make you smarter, especially if the free time spent on it would otherwise be spent on less academic pursuits.
A side note: "Dubious" comes from Latin, sharing the root of "duo", which means two,in this case referring to the possible indecision between two things or ideas.
Even in languages where the concepts can be encoded, it can be hard to determine what aspects of a given library are the encoding and which parts are the fundamental ideas if you haven't seen the ideas used in well-suited language. For instance, I didn't really understand the use of functools.reduce[0] or itertools.starmap[1] in Python until I was familiar with zipWith[2] and foldl [3] in Haskell.
The ideas themselves are not particularly complicated, but I hadn't previously worked with abstractions where the default was to operate on whole data structures rather than on individual elements, so I didn't see how you would set up your program to make those functions useful. In addition, for abstract higher-order functions, type signatures help a lot for understanding how the function operates. I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
It's now easy for me to use the same abstractions in any language that provides it because I only have to learn the particular encoding of this very general idea. While I couldn't figure out why functools.reduce was useful or desirable, I couldn't figure out many parts of C++'s standard template library at all. But if you already know the core concepts and the general way that C++ uses iterators and the fact that functools.reduce, Data.Foldable.foldl, and std::accumulate[4] are all basically doing the same thing for the same reasons is a lot more readily apparent.
> it can be hard to determine what aspects of a given library are the encoding and which parts are the fundamental ideas if you haven't seen the ideas used in well-suited language
That's a good point. Using a proper functional programming language doesn't just enable FP ideas (you can't fake a feature like implicit capture of variables), it may also clarify them by reducing baggage.
> I found `functools.reduce(function, iterable, initializer)` significantly more opaque than `foldl :: (b -> a -> b) -> b -> [a] -> b` because the type signature makes it clear what sort of functions are suitable for use as the first argument.
I suspect you're just a better Haskell programmer than me (I've only ever dabbled), but I find the big-mess-of-arrows syntax to be pretty confusing compared to a simple tuple of descriptively named identifiers.
Functional programming has been a game changer for me as well and has enabled me to write larger and more complex programs that are easy to maintain and reason around. I highly recommend cytoolz for python
This! What is funny that is that I started doing this before I knew anything at all about functional programming, I just started to avoid stuff that I had painful experiences with.
Later I read a couple of chapter of SICP and then I really changed and my programming hasn't been the same since. The language I use at work is JavaScript and while SICP isn't for JavaScript, nothing else has changed my JavaScript for the better to that degree.
> 1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
I am not very comfortable with this. How can I learn to do this in traditionally non-FP languages like Java? (Am CS undergrad student)
Caveat: I haven't touched Java in years, and that was not even a current version of Java at the time (well, it was old code made to run on the then-current JVM, but not utilizing any features introduced after 2006 or so). I'm assuming these are good resources, but I'm not sure.
I have experience with the same things in C# and other languages, the way they're using them in these articles are what I'd expect from a comparable API.
I can't speak authoritatively about Java, but it looks like map-reduce is available in Java 8 by casting a collection to a stream [0]. Considering the definitions of map and reduce can help one see how they can replace loops/counters:
MAP: Take a collection, say a list/array or a dictionary/hash, and perform some function on each member of the collection, returning a new collection who's members are the return values for each original member. It's a loop, but no loop!
REDUCE: Do the same thing as map, but carry along an output variable, and have your function's output for each member (potentially) mutate that output variable. Summing is a basic example.
I'm not specifically recommending preferring this in Java as a step towards functional programming. It's in, uh, more terse languages like Python and Ruby where the payoff is obvious [1][2]. And among not-functional programming languages, it's not just dynamic languages, either. Consider Dart (and seriously, consider Dart) [3]. Also, Javascript, which has had many features shoehorned-in over the years, has these and related functions.
One other other thing. Functional thinking has greatly changed the landscape of client-server applications that are hosted in the cloud as well. If your aim is apps, maybe don't bother to master the skills needed to set up and maintain a Linux server (although if you follow OP's other suggestions, you're well on your way). Instead, consider your backend as a network of microservices, functions, that each do one thing and do it with side effects only when necessary. The host for your app? Poof! That's AWS/GCP/Azure's problem.
One other thing. You will be thinking functions first if you get into data science, say with Python/Pandas. In general, Pandas functions are vectorized, meaning that they operate on members of a collection in parallel. You really don't want to write a loop that iterates over some 5,000,000 member collection and applies some expensive function serially.
1. Stop creating counters/loops and become facile with map, reduce, and the like. This will shift your thinking away from blocks and toward functions.
2. Take the time to really understand what side effects are, and start avoiding them everywhere they are not necessary. Keep scopes as local as is practical.
3. When you start toying with functional programming per se, make sure you really have your head around recursion. That's where much of the magic concision comes from.