Boost logo

Boost :

Subject: Re: [boost] Interest in an 'either' library?
From: David Sankel (camior_at_[hidden])
Date: 2013-06-26 12:21:04


On Tue, Jun 25, 2013 at 9:18 AM, Eric Niebler <eniebler_at_[hidden]> 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


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk