
C++17: I See a Monad in Your Future - adamnemecek
http://bartoszmilewski.com/2014/02/26/c17-i-see-a-monad-in-your-future/
======
dmunoz
Since the two main comments in this thread are unnecessary negative, I will
point out the ongoing discussion in /r/cpp:

[http://www.reddit.com/r/cpp/comments/1z1b1q/c17_i_see_a_mona...](http://www.reddit.com/r/cpp/comments/1z1b1q/c17_i_see_a_monad_in_your_future/)

The author of the post is also participating there.

~~~
dmm
Everyone in the c-preprocessor subreddit is surprisingly open minded about new
c++ features.

~~~
pbsd
I honestly don't understand the outrage here. This is about _standard library_
functionality, not new language features. As far as standard libraries go,
C++'s is actually pretty small and could use some additions.

~~~
agwa
(I presume) dmm is cracking a joke about how he thinks "cpp" should mean "C
preprocessor" and not C++.

~~~
cpeterso
Thanks for the explanation. My mind had been blown for a couple seconds. :)

------
npsimons
I'm of two minds about this: being moderately competent in C++ (my bread and
butter language), and a dabbler and True Believer in Better Languages (ala the
blub paradox), I think it might be nice and neat to have more high level
features in C++ (yay lambdas in C++11x!).

On the other hand, C++ is already a mess. It's not even a beautiful mess, but
it _is_ a very functioning mess. Adding more stuff to it just seems like
heaping more things on top of a pile that's already falling over in pieces.

Granted, like most everything else in C++, it will probably be optional, and
hey, if nothing else, having more (optional) features is very pragmatic.

~~~
jbeja
I wonder what programming language is not a mess?

~~~
steveklabnik
I'll just state the obvious: Scheme. ;)

~~~
jbeja
Everyone and their grandma recommends me Scheme, but i am more willing to try
a lisp that i can actually use in production like Clojure, i don't know.

~~~
steveklabnik
The assertion was "not a mess," not "usable in production." ;)

~~~
jbeja
Touché ;).

------
jerf
I guess the Katamari Dama-C++ is finally big enough to start picking up the
functional programming languages. Will the ball never stop growing?

~~~
stormbrew
... Start? C++ has been picking up functional programming paradigms pretty
much since its standardization and the inclusion of the STL in that standard.
C++'s evolution has been inexorably in this direction for a long long time.

~~~
bad_user
C++ has been an awful language for FP. First of all, you can't do FP without
lambda expressions. It's just way too painful to do it without lambdas.
Fortunately C++0x took care of that.

Then C++ is a too low level language. Memory management is a bitch to deal
with. FP is comfortable only in garbage collected languages, or otherwise you
need a really, really well designed library and unfortunately STL ain't it, as
STL has been plagued with memory leaks for years and still is. And many things
in it are simply broken.

For me the test for FP is pretty simple - a language is FP-capable if there
exists for that language a comfortable and reliable library for
persistent/immutable collections/data-structures. And this is in fact not
enough, but rather the bare minimum. You simply can't talk about functional
programming without immutable/persistent data-structures.

Can such a library be designed for C++? It's much harder to do it than in say,
a JVM language, or in Javascript, or in any garbage collected language,
because of the memory management. Persistent data-structures rely on
structural sharing for efficiency and in turn you rely on the garbage
collector to take care of the junk when no longer needed. As an exercise, try
to implement an immutable data-structure in C++, even a simple one like a
linked-list and watch in horror the output of Valgrind as you're using it.

I do think it's possible though, with a little care towards designing it. I
think the TFA explains quite well the value of good design and even takes
memory management into consideration. If more people actually do FP in C++,
maybe one day we'll have an awesome standard and an awesome library that we
could use for our FP needs.

With that said, I really hope they won't leave std::future as is and I hope
they do improve it. Adding a broken interface to the standard is not OK. If
you can't design well an interface, then adding it to the standard does more
harm then good. On the other hand the commute seems to be aware that
std::future needs fixing, so that's good.

~~~
BartoszMilewski
Funny you should mention persistent data structures in C++. That's exactly
what I've been working on. Here's my blog about leftist heaps translated from
Okasaki directly to C++: [http://bartoszmilewski.com/2014/01/21/functional-
data-struct...](http://bartoszmilewski.com/2014/01/21/functional-data-
structures-in-c-leftist-heaps/) . It follows previous ones about lists and RB
trees.

------
kenferry
I appreciate this article's clear explanation of the topics! I've yet to
really get my head around monads et al., but I could follow this article
through from beginning to end.

Thanks!

------
sixbrx
From skimming the article I can't tell if C++ will have anything akin to type
classes, which is how Monad is expressed in Haskell? Is that what "Concepts"
will bring and which enables Monad to be expressed as well?

~~~
Sharlin
The concept (lower case c) of monads cannot be represented in C++, but you can
certainly write monads in it like in just about any language. You just can't
make the compiler verify that what you have is actually a monad. As you
suspect, Concepts (capital C) should make it possible to type-check monads in
C++ like in Haskell).

~~~
dllthomas
You can't make the compiler check monad laws in Haskell, either. It would seem
to me you should be able to write a Monad template class, though I admittedly
haven't tried.

Edited to add: Ah, it seems a template argument can't itself be a template,
which would seem to rule it out (though I'm running on very little sleep, so I
might very well be wrong).

~~~
ajtulloch
You can use templated template parameters in C++ -
[http://stackoverflow.com/questions/213761/what-are-some-
uses...](http://stackoverflow.com/questions/213761/what-are-some-uses-of-
template-template-parameters-in-c)

~~~
dllthomas
Ah, so you can! Here's the list monad. I'm not sure the type unification on
templates is sophisticated enough to get all of the niceness of Haskell monads
(I wasn't able to get bind to work generically, for instance, and I'm not sure
what would happen with another instance of pure differentiated only by return
type...) and certainly there's no do-notation), but clearly the functionality
can be expressed. Still fun. It prints the results of expressions prefixed by
their (loose) Haskell equivalents. Applicative included for good measure.

    
    
        #include <iostream>
        #include <list>
        #include <functional>
        
        
        using namespace std;
        
        // fmap :: (a -> b) -> f a -> f b
        template <template<typename> class F, typename A, typename B>
        F<B> fmap(function<B (A)>, const F<A> &);
        
        // pure :: a -> f a
        template <template<typename> class F, typename A> F<A> pure(A);
    
        // appl :: f (a -> b) -> f a -> f b
        template <template<typename> class F, typename A, typename B>
        F<B> appl(F< function<B (A)> >, F<A>);
        
        // join :: m (m a) -> m a
        template <template<typename> class M, typename A>
        M<A> join(M< M<A> >);
    
        // mbind :: m a -> (a -> m b) -> m b
        template <template<typename> class M, typename A, typename B>
        M<A> mbind(M<A> &x, function<M<B> (A)> f);
        
        template <typename A, typename B>
        list<B> fmap(function<B (A)> f, list<A> xs) {
            list<B> ys;
        
            for(auto x : xs) {
                ys.push_back(f(x));
            }
        
            return ys;
        }
        
        template <typename A> list<A>
        pure(A x) { return { x }; }
    
        template <typename A, typename B>
        list<B> appl(list< function<B (A)> > fs, list<A> xs) {
            list<B> ys;
        
            for(auto f : fs)
                for(auto x : xs)
                    ys.push_back(f(x));
        
            return ys;
        }
        
        template <typename A>
        list<A> join(list< list<A> > xss) {
            list<A> ys;
        
            for(auto xs : xss)
                for(auto x : xs)
                    ys.push_back(x);
        
            return ys;
        }
        
        template <typename A, typename B>
        list<A> mbind(const list<A> &x, function<list<B> (A)> f) {
            return join(fmap(f, x));
        }
        
        template <typename A> ostream &operator<<(ostream &out, const list<A> &xs) {
            int i = 0;
        
            cout << "[";
        
            for(auto x : xs) {
                out << (i++ ? ", " : " ") << x;
            }
        
            cout << " ]";
        }
        
        int main() {
            list<int> xs = {1,2,3};
            function<double (int)> halve =
                    [] (int x) -> double { return 0.5 * x; };
    
            function<double (int)> quarter =
                    [] (int x) -> double { return 0.25 * x; };
    
            function<list<int> (int)> repeatOnce =
                    [] (int x) -> list<int> {
                        list<int> ys = { x, x };
                        return ys;
                    };
    
            function<list<int> (int)> repeatIfEven =
                    [&] (int x) {
                        if(x % 2) return pure(x);
                        return repeatOnce(x);
                    };
        
            list< function<double (int)> > fs = { halve, quarter };
        
            cout << "return 7 = " << pure(7) << endl;
            cout << "xs = " << xs << endl;
            cout << "fmap halve xs = " << fmap(halve, xs) << endl;
            cout << "fmap repeatOnce xs = " << fmap(repeatOnce, xs) << endl;
            cout << "fs <*> xs = " << appl(fs, xs) << endl;
        
            cout << "xs >>= repeatOnce = " << mbind(xs, repeatOnce) << endl;
            cout << "xs >>= repeatOnce >>= repeatOnce = "
                    << mbind(mbind(xs, repeatOnce), repeatOnce) << endl;
            cout << "xs >>= repeatIfEven = " << mbind(xs, repeatIfEven) << endl;
        
            return 0;
        }

~~~
detrino
Nice result :)

Btw, it's tempting to use std::function in the same way as you would use a
function in Haskell, but it usually isn't a very good idea. In C++
std::function is for when you want type erasure, usually because you wish to
store a functor somewhere with a uniform type. Type erasure in C++ is similar
to existential typing + a type class in Haskell. You could say that all
Haskell closures are implicitly using type erasure and then the compiler has
to work hard to optimize away the extra boxing/indirect calls.

Here is another way to write map in C++:

    
    
        #include <iostream>
        #include <list>
    
        template<typename F, typename A, typename B = typename std::result_of<F(A)>::type>
        std::list<B> map(std::list<A> const & xs, F f)
        {
            std::list<B> ys;
            for (auto const & x : xs) ys.push_back(f(x));
            return ys;
        }
    
        int main()
        {
            std::list<int> xs{1, 2, 3, 4, 5};
            auto ys = map(xs, [](int i) { return i * 2; });
            for (auto y : ys) std::cout << y << "\n";
        }
    

You can expect this code to run exactly as fast as if you had written the loop
by hand instead of using the map template function.

~~~
dllthomas
Yeah, wasn't meant to be idiomatic C++ code, just a simple proof-of-concept.
If you'd care to translate it into the former I'd be fascinated to see the
result. Playing around a bit, I've been having trouble expressing the type of
mbind when I make the function type a parameter to the template. I'm 70% sure
it's my failure, not the language, though.

------
Locke1689
Minor nit, but I believe that his description of the async pattern in future
is not only monadic, but also comonadic.

~~~
theophrastus
i prefer "holoambiadic".

in all relative seriousness, would anyone care to put forward a concise
definition of "monad" (for programmers, not math-category-theorists)?

~~~
spion
Functor is something that has a map function.

A monad is any generic class that has a flatMap function and a factory
function that together satisfy 3 laws (explained below)

flatMap is like map, except the function passed to flatMap must return a monad
(of the same kind).

The factory can make a monad of a regular value (it wraps it)

For example, an array's flatMap function takes a function that takes an
element and returns an array. You can return an empty array if you wish, or an
array of two elements (of the same type). flatMap "flattens" the result - it
returns an array of elements of that same type.

The factory function and flatMap must satisfy 3 laws: left identity, right
identity and associativity. That is, flatMapping with the factory function
should result with the same thing as what you started with; wrapping a value
with a factory then flatMapping it should result with that same value being
passed to the function you passed to flatMap; and it shouldn't matter whether
multiple flatMap calls are chained, or nested.

Like design patterns in OO languages, it isn't immediately clear why its
useful to give a name (or a type) to all generic classes that satisfy this
"interface" called monad, but it turns out that the concept pops up
everywhere, from arrays to futures to Maybe (to replace null values) to Either
to Observable and so on.

Not sure if I managed to be correct, understandable, concise or neither :P

~~~
theophrastus
ok i'm going on a limb here and suggest that you're probably "correct". but
you need to use more terms selected from the following list (random usage is
encouraged but idempotent): closure, adjoint, operator, endofunctor, universal
algebra, maybe, iteratee, continuation, surjective, type constructor,
simonpeytonjonesing

------
im3w1l
>Combining functions using next, unwrap (or, equivalently, bind), and
make_ready_future is equivalent to specifying data dependencies between
computations and letting the runtime explore opportunities for parallelism
between independent computations.

He wants a jit for cpp?

------
nly
What I would like is a way select() over a set of futures.

~~~
sanderjd
How would that be different than the `when_any` function the author described?

~~~
MBCook
I believe the difference is that you wouldn't have to then iterate over all
the futures to see which one fired, it would just return one of them.

Boilerplate prevention, I think.

~~~
sanderjd
Thanks, somehow I forgot about the author's complaint about the API of
`when_all` between the time I read it and the time I wrote my comment.

------
angersock
Why are all the Haskell and functional folks trying to screw up C++?

I'm sure that's not the actual state of things, but _goddamned_ if that isn't
what appears to be happening.

EDIT:

Before downvoting, consider the elaboration I've replied below with (and then
you can at least downvote that too!).

EDIT2: HN wouldn't let me submit this, so is edit:

The basic issue is not that futures/promises/monads aren't awesome--I'm quite,
quite sure that they are the future (harhar) for solving a lot of things. I
use them extensively, for example, in Javascript. They're Good Things (tm).

It's not that template metaprogramming is bad, or not useful, or that Boost
isn't our lord and savior for greenfield C++ code.

 _The problem is that this shit still has to interoperate with code written
more than two decades ago._

C++ began life as a weird ball of OOP and templates slathered over C. It
failed to fix some of the real, true, awful parts of C (pointers, for
example), and those are still around.

Pray tell me, why do I want a language which has both atomic exchanges (sorta,
kinda, depending on your compiler and environment) and monads and function
mapping? Doesn't that strike anyone else as, oh, I don't know...ill-focused?

C++ is simply too large a language to recommend to a beginner, too difficult
to actually make guarantees about (when you can at all!), and in general the
whole rotten mess should be cut in half.

Into, perhaps, C, and Haskell.

~~~
wereHamster
What exactly are your objections?

Is it the naming convention? Functor, Monad, Applicative, bind, join, fmap
etc? Yes, Haskell can be a bit confusing at first. But if you don't like them
you can urge the standards committee to use different names.

Or is it the concepts themselves? That would be pretty sad. A Functor is a
really basic concept. You probably use it every day, in one form or another.
I'm pretty sure of that. Once you understand this basic concept, you can start
building the others on top of that knowledge. It's all about building a common
language and understanding of these basic concepts.

~~~
AnimalMuppet
You know, if C++ used the concepts and just put sane (non-abstract-algebra)
names on them, that might be really useful. I might get Haskell a lot better
if the C++ committee does this well.

~~~
chongli
What's wrong with the names? I see this complaint everywhere but not a single
person has yet managed to explain to me what's wrong with them. Is it because
they're from math? Perhaps we should rename them after dog breeds or
something.

~~~
freyrs3
Nothing is wrong with them. A lot of people aren't used the level of
abstraction that Haskell uses, so they look at a specific instance of the
structure (often the List type) and think of a name that fits that instance
ignoring the more general class. You end up with names like Appendable (
Monoid ) or FlatMappable ( Monad ) both of which give horrible intuition for
the general case.

I think the abstract algebra names are great because they describe the general
case precisely without any preconceptions carried along with them.

~~~
dllthomas
_" I think the abstract algebra names are great because they describe the
general case precisely without any preconceptions carried along with them."_

And there's papers about interesting things you can do with them.

------
malkia
I for one welcome our muddy::future!

------
clinq
This is a great example of how theory enthusiasts ignore practical world.
Think about similar ironic claim by a C++11 guy: Haskell, I see a lambda in
your future!

------
eli_gottlieb
Please no. I liked C. Watching it die the death by a thousand cuts that is C++
featuritis is painful.

~~~
jessaustin
Eh, C++ was always a bit of a lost cause with respect to "featuritis", wasn't
it? It's not like you can't still use C.

