Functional Programming in Modern C++: The Imperatives Must Go [video]
youtube.com
youtube.com
Most so called functional methods in the standard library require mutating the target (ie std::sort, std::transform, etc.) They are not ergonomic because we have to pass begin and end as arguments, which also means they cannot be chained.
Lambdas are hard to work with because it is difficult to track the lifetime of the values they borrow.
std::variant, std::visit, std::optional have a clunky interface. And because they are not part of the language, they often make it difficult to represent structures that would be trivial in languages with tagged enums (for instance a tree or a Linked List are difficult to model with std::variant or std::optional.) The compiler is also not able to assert that data is present in an option or that the correct variant is accessed.
I think wrapping stuff in std::function may also prevent inlining? I'm not sure how much link-time optimizations can help with that, but I'd guess languages like Haskell do better in this area.
Also, C++ doesn't even have a function composition operator :/ The presentation starts off with "function application is the essence of FP", but it's really function composition. By his definition, C is a hell of an FP language.
Modules aren't going to help with template instantiations.
What it has to do with modules is you can effectively treat the module as a mini 'shared library' and when a template is instantiated you can append the compiled code into the module and re-use it in other compilation units.
Why can't you, I don't believe the standard says anything about the intermediary compiled unit of the module that would block it from doing so, I haven't dug into the implementations but I'd imagine they effectively create a static library analog of the compiled code and some analog of a header file for use in other compilation units, so say you're wanting to use std::vector<int> it can just look and see if that's already present in the module, if not instantiate it and then write it to the 'static library analog'
[1] https://stackoverflow.com/questions/12645254/ghc-code-genera...
Meanwhile while actual F programmers, and the developers of non-FP languages that receive these claims (like Lisp or C++) aren't interested in making these kinds claims.
It's fandom.
Like many languages called "functional" (rust being one of them) they are not functional at all. However, they have enough uh...functional-ity that can be leveraged to create code that is sometimes easier to reason about. I don't really understand the aversion to imperative programming. There's certainly a level of cargo cult. Add to that the general difficulty of pure functional programming and now you've got a cult complete with it's own ivory tower.
It's once again a question to find the ultimate kitchen sink. C++, being one of the first kitchen sinks, doesn't surprise me as the subject of this article.
Ironically Ada 202X might be easier to implement than C++23.
For one, just having lexically scoped closures as first class citizens doesn't cut it in my eyes. Even though perhaps in some contexts and timelines this would suffice to say that your language (or paradigm) is functional.
In JS using closures was the tool to create modules and objects to maintain internal state. It really was closer to some version of OO than to functional programming, because you would mutate and construct state with these.
Personally I'm not a purist in any sense. I'm all about the interface:
- Does the thing I'm passing you change under me?
- Is the thing you return the same if I give you the same arguments?
Those are the qualities I really care about. In some cases I would even go as far as accepting you violate the former guarantee as long as I'm not using the argument anymore (you own it). What I don't care about is what you do under the hood.
> I don't really understand the aversion to imperative programming.
This kind of functional quality really shines in certain contexts.
For one, a function (in that sense) is easy to reason about and trivial to test.
Secondly, if you think of functions as (lazy) questions about your program state, you avoid memoizing and duplicating state all over the place (as you would with a full-on imperative/OO approach), which can have positive effects on memory pressure, GC etc. You avoid passing around pointers and instead construct normalized and write optimized data structures that can be asked questions when needed.
A thing that follows is ease of concurrency. You now have a better separation of mutations and reads. The concurrency semantics trivially fall out of that.
C++20:
for (int i : ints | std::views::filter(even) | std::views::transform(square))
std::cout << i << ' ';I haven't used it yet, but C++ Streams[1] is a header-only library that appears to have great ergonomics and an API that will feel familiar for those who know Java Streams. Since I haven't had the luxury of being able to use C++20 yet, I was unfamiliar with the ranges library. Starting at 1:06:00 you can see some motivating examples for those and they look great to me. Much easier to read than the equivalent STL.
The libraries mentioned in the video TartanLlama/optional[2] and TartanLlama/expected[3] clearly demonstrate the value proposition (brevity and clarity) on their github pages.
[1] http://jscheiny.github.io/Streams/
Complexity doesn't magically go away by choosing a different programming paradigm - problems are just shifted into another direction.
What must go are the holy warriors who are stuck on their dogma and do not realize that the world does not fit into their heads.
Unless you're arguing that converts to the FP religion feel an...imperative to convert others...
https://steve-yegge.blogspot.com/2006/03/execution-in-kingdo...