Boost logo

Boost :

From: Dave Gomboc (dave_at_[hidden])
Date: 2002-12-12 21:19:35


Adjusted at five points; please ignore the previous one.

---------- Forwarded message ----------
At this point in time, I vote to reject the library. My vote is based
upon a review of the interface and implementation in optional.hpp of the
zip file I downloaded from the link posted by Fernando on Dec. 12, and
following the discussion via the mailing list digests. I will qualify my
vote by indicating that this is my first review for boost.

The interface is undergoing a considerable amount of flux during the
review period, and I think that significantly more change is still
required before I would vote for its inclusion.

The operators that (unsuccessfully) attempt to provide pointer semantics
are misleading, and should be removed. To me, appeals to optional being a
container (or giving it pointer semantics, as if it were an iterator!)
are misguided.

In my opinion, optional<T> [which perhaps ought to be may_be<T> or
maybe<T>, as an earlier poster suggested, also see below] works best as
denoting a type T', whose assignable values are the legal values for type
T unioned with the value "nil", or "nothing" (as in Haskell). (It
shouldn't be called "uninitialized", because that would be misleading.
"nil" is a perfectly valid value for a variable of type T' to take.)

Because "nil" is just another value, operator== can be defined
straightforwardly. If both are "nil", return true; if exactly one is,
return false; otherwise the normal comparison value is returned.

operator< could be left undefined, but it is simple enough to arbitrarily
choose that "nil" is strictly less than every other value. I don't see a
downside to this, and it would ensure that T' is a strict weak ordering
whenever T is, which is useful for STL usage.

The operators !=, >, <=, and >= directly follow from definitions of the
above two operators.

Conversion from T' to T is straightforward except when the value of type
T' is nil. In this case, the developer may want to throw,
default-construct the value of T, or assert. Template policy choice?
(Perhaps that's overkill.)

One could possibly provide a test for the nil state by declaring an enum
(in some scope) that defined some (arbitrary) value to "nil", then
providing an operator== that allowed comparisons on that enum type, to
allow code such as:

using boost::may_be;
...

may_be<int> blah(void) {

    may_be<int> x(5); // x is constructed to value 5
    may_be<int> y; // y is default-constructed to value nil
    ...
    if (y != nil) { ... };
    ...
    x = nil;
    ...
    return std::max(x, y); // defined since std::less uses operator<
};

It is possible, but not necessarily desirable, to include "is_nil()" or
"is_not_nil()" functions. operator!() already exists in Fernando's code
as the equivalent to "is_nil()"; a double invocation (e.g. "!!x") would
be equivalent to "is_not_nil(x)". I suspect it is probably not worth
fattening the interface for them, but this may be a matter of taste.

(Also, below is a message I had sent to Fernando already, but I suppose it
might as well be sent to boost also.)

Dave

---------- Forwarded message ----------

"Maybe" is a built-in type constructor in Haskell, which AFAIK is the most
widely used functional programming ("FP") language. It is used to denote
exactly what your suggested optional<T> is. (Which is why I am
recommending use of "maybe<T>" or "may_be<T>" instead of optional<T>.)

An example in Haskell syntax:

    safe_division :: Float -> Float -> Maybe Float
    safe_division x y =
                        if y == 0 then Nothing
                                  else Just (x/y)

Consider this Haskell declaration:
    get_maximum_value :: BTree Int -> Maybe Int

The equivalent (free function) declaration in C++ would be:
    may_be<int> get_maximum_value(const BTree<int> &);

Yet another function prototype:

    get_cached_value :: a -> [(a, b)] -> Maybe b

which can return Nothing if the function does not have "a" as the first
item of any of the pairs in the list.

Maybe is defined in Haskell as

    data Maybe a = Nothing | Just a deriving (Eq, Ord, Read, Show)

(here "a" is an arbitrary type; "Nothing" is like "nil", "NULL",
"uninitialized", or whatever you want to call it.)

As an example, currently the C++ standard includes
    T & stack<T>::top(), with precondition !(stack.empty()).

Instead, it could be
    may_be<T> & stack<T>::top(); // no precondition required

<set> could be improved also, instead of:
    pair<iterator, bool> set::insert(const value_type & x)

we would use
    may_be<iterator> set::insert(const value_type & x)

where the return value is initialized to the place of insertion if x
was inserted, or is uninitialized if x was not inserted.

Of course, it is possible to use variables of type may_be<T>:

int blah(int x) {

    may_be<int> y; // default-constructed to uninitialized, right?
    .
    .
    .
    // expressions involving y here are okay instead of invalid.
    .
    .
    .
};

(The use as an optional function parameter is no doubt what you had in
mind in the first place, and where you got the "optional" name from.)


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