
FunctionalPlus – helps you write concise and readable C++ code - Dobiasd
https://github.com/Dobiasd/FunctionalPlus
======
ajtulloch
I would recommend instead Facebook's folly::gen library, which provides these
functional combinators in a high-performance fashion, by expressing them as
streaming combinators. You get all the map/filter/take/drop/reduce primitives,
but in a much cleaner way.

See
[https://github.com/facebook/folly/blob/master/folly/gen/test...](https://github.com/facebook/folly/blob/master/folly/gen/test/BaseTest.cpp)
for an example.

~~~
moonchrome
I see all these nice C++ libraries like Facebook folly and then I see that
they bring in boost and megabytes of include files for trivial stuff bringing
in stuff I absolutely don't need (or ever will, in fact most of it is for
dealing with legacy compilers and OSes I don't care about). Maybe it's just me
but I don't like half an hour compile times. Am I missing something or has
everyone else using it just accepted the compile times ?

~~~
jzwinck
"Bringing in Boost" doesn't cost you much at all in terms of compile time.
Using some of the advanced template meta-programming stuff in it may, but
basic things like the legacy compiler and OS support you mention are largely
using the C preprocessor and take very little time. In other words, give it a
try--I'm sure you won't have half an hour compile times just by using Folly.

If you want to compile C++ (or C) really fast, look at icecc:
[https://github.com/icecc/icecream](https://github.com/icecc/icecream)

------
jzwinck
One of the main reasons to use C++ today is the potential for high
performance. One of the examples given is a great demonstration of what not to
do: allocate a new container on the heap, scan it once, and delete it. I'm
talking about the "I in team" example, which if you coded it by hand would
almost certainly use a better algorithm. It's hard enough teaching people to
avoid inefficiency when it's right there in front of them, and harder still
when you encapsulate inefficiency in a pretty package. This is why some
"obvious" functions don't exist in the STL.

~~~
Ace17
Actually, teaching people how to avoid inefficiencies might transform "slow
code" to "slow and unmaintainable code".

It's more strategic to teach people to write readable code (exactly like the
OP does), and then ... to teach them how to use a profiler.

~~~
jzwinck
How is a profiler going to help if your code is riddled with inefficient
structures? The "I in team" example can be made more efficient if SplitWords
returns an iterator, but SplitWords itself won't be possible to change once it
is ingrained in a codebase (because e.g. some call sites will come to depend
on it returning a list).

I love profilers as much as the next person, but if you build a system from
inefficient building blocks, you may eventually need to build it again. But in
a professional setting you are likely to be told not to do build it a second
time, or you will be gone before that time comes, leaving someone else to
clean up your moribund codebase of a thousand inefficiencies.

~~~
dkersten
Make it work, then make it fast.

I hear a lot of people complain about higher levels of abstraction in
languages like C and C++, because they take away control or don't allow them
to squeeze every last drop of performance out of their programs.

I also see a lot of software in the wild that was programmed in C and C++ and
runs very fast... but is buggy as hell or has pretty nasty security bugs.

The fact is that, regardless how good you think you are, most likely you're
not one of the few people who can write fast, bug-free[0] software in C and
C++. I certainly am not despite having written quite a bit of C and C++ in the
over the years. The amount of high-impact bugs (especially security bugs, eg
Heartbleed)

[0] Where I really mean: no severe bugs (data corruption, data loss, security
flaws)

------
makecheck
The examples are clearly achieving the goal of "concise". I would counter with
a different style though, because I try to imagine how the code would change
over time and how it would fare when given constraints on memory and runtime.

Compare:

    
    
        for (int x : numbers) {
            if (1 == (x % 2)) {
                odds.push_back(x);
            }
        }
    
        auto odds = KeepIf(IsOdd, numbers);
    

To me, the first case is preferable. The 2nd case uses custom functions that
may not be familiar to the maintainer, and semantics are clearly hidden (e.g.
does KeepIf() modify the list, return a copy, or return some kind of
intelligent generator object that still depends on the lifetime of
"numbers"?).

Suppose I discover that my application starts crashing with the odd number 15
and I have to work around that case very quickly. The loop version above is
trivial to modify, whereas I have _no idea_ how the 2nd version would be
modified to filter out one value! And if I want to do several things with the
numbers while they're being found (e.g. print them out), the loop is easy to
change but the 2nd version basically has to be replaced entirely (and
replacing even a concise expression is risky due to the issues above, e.g.
unclear semantics).

Now compare approaches for another case:

    
    
        if (FindWord("I", team)) ...
    
        if (Contains("I", SplitWords(team))) ...
    

This shows a general problem with a "functional style" at times: it's placing
constraints on the implementation that don't need to be there.

To me, the first style is preferable because FindWord() could easily walk a
raw string and _stop_ as soon as it finds the target word, with a chance for
better-than-N average complexity. The second case would mandate: copying
strings, visiting all characters in the string every time, and revisiting at
least some of the characters in the form of words.

Finally, I think that the C++ standard algorithms, lambdas, and compact-"for"
do allow a lot of these tricks to be avoided. Consider algorithms like
std::all_of() for instance, which can be combined with lambdas to produce the
same results as examples like "AllBy(And(..." but without the downsides I
mention at the beginning of my comment.

~~~
Dobiasd
I really like your remark about the performance of

    
    
        if (Contains("I", SplitWords(team)))
    

so I added it to the README.

If you don't want the 15 in the odds, just to this:

    
    
        auto goodOdds = without(15, keep_if(is_odd, numbers));
    

The fact, that `keep_if` does return a copy can easily obtained by looking at
the function signature or the type of `odds`. Both things should not be
complicated with a modern IDE.

I agree with you in general. The library is not made for performance-critical
stuff.

But the "raw loops etc. are easier to understand by outsiders" seems like an
argument agains using any libraries at all.

~~~
SamReidHughes
> But the "raw loops etc. are easier to understand by outsiders" seems like an
> argument agains using any libraries at all.

It's a trade-off.

------
Dobiasd
While working on my last projects I accumulated a collection of pure functions
I needed now and then. So I thought, why not write a short pitch as a README
and make it public? So here it is. It would be very nice of you if you could
tell me what you think, so I can learn and improve.

~~~
frozenport
I generally use python when manipulating lists, and C++ for floating point
oriented tasks.

~~~
Dobiasd
What do you do if your application needs to do both?

~~~
frozenport
Thats a great question!

In one case I used a python wrapper around a C++ core, but when it came time
to re-write it we switched to pure python and have so far hit our performance
benchmarks - mostly because libraries like OpenCV or PyCuda are now able to
efficiently wrap our critical paths.

