Boost logo

Boost :

From: Brian McNamara (lorgon_at_[hidden])
Date: 2003-08-30 22:16:28


On Sun, Aug 31, 2003 at 09:58:45AM +0800, Joel de Guzman wrote:
> Fernando Cacciola <fcacciola_at_[hidden]> wrote:
> > Hi Mat,
> >
> > Thanks for the input.
> >
> > optional<> is now out on the field and it is the only utility of its
> > kind I've ever seen in C++, at least in real use. This has the
> > drawback that there is very little experience with it. Therefore, in
>
> There's a lot of experience with it in other languages. Why not
> leverage that? Haskell::Maybe for instance.

I feel like Maybe works more smoothly in Haskell than in C++, but I
haven't ever really tried to nail down the reasons why. This thread has
me thinking about it more, though, so I'll share some general thoughts
(as well as some suggestions for "optional" at the end).

Two issues spring to mind immediately: pattern matching and monads.

Pattern matching:
Haskell's Maybe is a type constructor, and thus the value constructors
Just and Nothing can be used in pattern matches. Pattern
matching provides a common way to do the "test" and the "unwrap"
steps. Code like

   optional<int> o;
   ...
   if( o )
      do_something( *o );
   else
      maybe_do_other();

in C++ sometimes takes a form like

   o :: Maybe int
   ...
   case o of Just x -> doSomething x
             Nothing -> maybeDoOther

in Haskell. Since this form of using pattern matching to "case-ify" and
deconstruct a datatype is a standard idiom in Haskell, nothing special
needs to be done for Maybe.

Monads:
In addition, Maybe forms a monad, which means that you can use
do-notation to string together computations involving Maybes. Code like

   optional<int> x, y, z, result;
   ...
   if( x ) {
      y = foo( *x );
      if( y ) {
         z = bar( *x, *y );
         if( z )
            result = baz( *x, *y, *z );
         else
            result.reset(); // being more explicit than I have to be
      }
      else
         result.reset();
   }
   else
      result.reset();

can be written as

   x, y, z, result :: Maybe int
   ...
   do x <- x
      y <- foo x
      z <- bar x y
      result <- baz x y z
      return result

in Haskell, which uses the Monad's bind operation to sugarize over all
the if-then-else plumbing to push Nothings all the way through the
computation. In FC++, we have monad comprehensions, so it could be
written as just

   maybe<int> x;
   ...
   fcomp_m<maybe_m>()[ baz[X,Y,Z] | X <= x,
                                    Y <= foo[X],
                                    Z <= bar[X,Y] ]
                       
Comprehensions are unlikely to be used much in normal C++ code, though.

In any case, I suppose my point with both of those issues is that
Haskell already has some kind of standard/idiomatic way to sugarize over
the details of dealing with Maybes. I don't think we have anything
equivalent in C++, so it's not clear to me what we can learn from
Haskell when trying to design the interface for optional in C++.

Offhand, the "pointer interface" for reading the value of an optional
seems good to me. Being able to say

   optional<int> x;
   ...
   if( x ) foo( *x );

seems like a natural way to "sugarize" this in C++.

As for the constructors, why not have a separate function for
initializing optionals, a la Just/Nothing in Haskell? I am imagining
something like this:

   template <class T>
   class optional {
      optional(); // or maybe public, depending on preference
      optional( const T& ); // private
   public:
      optional( const optional& ); // still copyable,
      optional& operator=( const optional& ); // assignable
      ... operator bool ... operator*
   };
   template <class T>
   optional<T> just( const T& ); // the constructor (a friend)

Then you no longer have the issue of

   optional<int> x(3);
   int r = *x;
   optional<int> y = 4;
   r = *y;

looking "funny" or "unbalanced", because now it's

   optional<int> x( just(3) );
   int r = *x;
   optional<int> y = just(4);
   r = *y;

and the "just" balances out the "*", in a way.

(This is similar to what we do with maybe<T> in FC++, though we don't
have the op-bool/op-* sugar for reading maybes. In FC++, the constant
NOTHING can also be used to initialize an "empty" maybe.)

-- 
-Brian McNamara (lorgon_at_[hidden])

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