
On Tue, Jun 25, 2013 at 9:18 AM, Eric Niebler <eniebler@boost.org> wrote:
On 6/24/2013 8:55 PM, David Sankel wrote:
If we really care about adding a do syntax EDSL, why not make it general purpose like Haskell's version so that it can work with any monad and compose better with other things?
either<error, int> result = do( set( _x, getInt ), set( _y, getInt ), doReturn( pure( _x + _y ) ) );
If we're going to do that, lets do it generically and completely.
I've been giving serious thought to that recently. The problem with it, however, is that it requires the use of Phoenix-style lambda expressions like "_x + _y". (I can only assume you mean for _x and _y to be placeholders and not actual values. I can't think of another way to implement this.) It seems that for such core functionality, we don't want to be saddled by Phoenix's syntax and compile-times, but maybe I'm wrong.
I think the 'index' syntax would work around the issues with both unfamiliarity (people already know bind) and phoenix compilation times.
Anyhow, yes, I think something like this should exists. Phoenix could provide one implementation. FWIW, I just pushed another monad-in-c++ up to my github last night (the first link is the state monad, the second is an example that uses it):
https://github.com/ericniebler/fpxx/blob/master/include/fpxx/monad/state.hpp https://github.com/ericniebler/fpxx/blob/master/example/state.cpp
Cool. I hope you gave bind and '>>' names so they can be used in higher order functions. Let me give that do notation I mentioned in the earlier email a better definition: do( stmt1, stmt2, stmt3 ). Do will return some monad parameterized by a type A. stmt1 will define that type. Every statement will be an expression that returns a monadic value (assuming a monadic context). Here are the expression possibilities: - <normal c++ expression>. For example make_optional( 3 ), or either<std::string,int>( "Hello World" ) would return monadic values. 3, and "hello" would not. - pure( e ). This will wrap the expression into the current monad and return the result. - app( e1, e2, e3, ... ). Applies the function returned by e1 to the values returned by e2, e3, etc.. - _n, gets the unwrapped value of any previous statement. And optionally, we can add do(...) as another expression type who can refer to indices in the parent do using de-bruijn indices (_2_1, for example would get the unwrapped value of the first statement of the parent). A common case: either< Error, std::string > result // or whatever monad you like = do( openNetworkConnection( "192.168.0.1" ), app( sendMessage, _1, "What is your name" ), app( getLine, _1 ) ); A somewhat contrived complex example: either< Error, int > result = do( getIntFromUser(), getIntFromUser(), app( convertOptionalToError, do( app( sqrt, _1_1 ) app( sqrt, _1_2 ) pure( app( plus, _1, _2 ) ) ) ) ); where either<Error, int > getIntFromUser(); // This would need to be wrapped into a struct, of course. template< typename T > either< Error, T > convertOptionalToError( optional< T > ); boost::optional<int> sqrt( int ); int plus( int, int ); Interestingly enough, normal C++ expressions that return exceptions can be considered a form of the either monad. I don't know if that fact is useful or not. But I don't think this obviates the need for the try_call that I was
suggesting. It is extremely light-weight and let's people write ordinary C++ instead of Phoenix. And I think it would be easier to teach to C++ programmers.
I dunno, it looks pretty odd to me. Do notation seems both simpler and more powerful. (Just to note for other readers, I tend to write complex examples to illustrate the power - simple things remain simple though)
I honestly think that if Either goes into Boost, we shouldn't consider
it as a value or error structure at all. Expected (will) do the job.
Seems a bit excessive to me to have both. If we get Expected, then Either is unnecessary -- it's just one template alias away from variant. I don't consider the syntactic niceties of "left" and "right" to be compelling, and if folks want it, we can provide left and right free functions for 2-arg variants. But IMO automatic error propagation is the *only* thing that can make this proposal compelling.
I personally find myself using std::pair frequently even though it is also just one template alias away from std::tuple. I still think there's a case for exposing these commonly-useful simple building blocks. Incidentally, the std::pair monad is also interesting.
I use it too. But I tend to believe that if C++ got std::tuple first, std::pair really would just be a template alias for a 2-tuple. That's obviously just my opinion, though.
I'm down for making either a template alias for a 2-variant where the 2-variant has special names to give easy access to left and right. -- David Sankel