Boost logo

Boost :

Subject: Re: [boost] [optional] Safe optional
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2014-11-21 02:06:36


Le 20/11/14 23:42, Olaf van der Spek a écrit :
> On Thu, Nov 20, 2014 at 11:49 AM, Andrzej Krzemienski
> <akrzemi1_at_[hidden]> wrote:
>>> http://www.boost.org/doc/libs/1_57_0/libs/optional/doc/html/boost_optional/quick_start/storage_in_containers.html
>>>
>>> By containers you specifically mean map, right?
>>> Unordered map for example would not require operator<
>>>
>>> Containers (maps) always come up as the primary rationale for operator<
>>> However, the user could also define his own comparison function for
>>> this purpose.
>>>
>>> Perhaps other use cases of operator< could be presented.
>>>
>> I was trying to illustrate how it is useful to think of optional<T> as
>> extending the domain of T (rather than representing "either T or error").
>> Not to illustrate the storage in containers.
> Optional really is either T or none (no value) isn't it?
>
> What do other languages do? Do they have something like optional? How
> are comparisons defined?
In Haskell
data Maybe = Just T | Nothing

You can not convert a maybe implicitly from a T (the concept doesn't
exist in the language as AFAIK). You need to use the constructor Just

x = Just 1
y = Nothing

This is equivalent to our

auto x = make_optional(1);
auto y = none; // nullopt

You can extract the value of a Maybe by pattern matching. IIUC, that
means that you need to consider all the cases, as if we have a switch on
an enum and the compiler forced us to have the case for all the enum
literals.

zeroAsDefault:: Maybe Int -> Int
zeroAsDefault mx= case mxof
     Nothing-> 0
     Just x-> x

We don't have yet pattern matching in C++. This pattern matching is not
equivalent to the optional::value function.

We could have a match function that do this in C++14 (maybe C++11)

// zeroAsDefault:: Maybe Int -> Int
auto zeroAsDefault(optional<int> mx){
        return match(mx,
     [] (pattern<none_t, nothing>) {return 0; }
     [] (pattern<int, just> x) {return x.value(); }

For those that know tagged<T,Tag>, pattern has the same role.

We could surely have also something like

auto zeroAsDefault(optional<int> mx){
        return match(mx,
     [] (nothing) {return 0; }
     [] (just<int> x) {return x.value(); }

Note that the call to x.value() is safe as we know by pattern matching
that pattern<int, just> or just<T> has a T.

Maybe is Eq and Ord if T is.

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

I don't know how the Minimal Complete Definition for Eq and Ord are defined, but in any case Maybe T and T don't compare.
Just T and Nothing compare however.

value_or_eval is called maybe
value_or is called fromMaybe
value_or(x) = value_or_eval(id(x))

Clearly the safe_optional we are talking of is related to the Haskell
Maybe type.

In addition Haskell Maybe can be a Functor, an Applicative, an
Alternative, a Monad, a ... depending on the type T. It is all this
additional functionality that render the use of Maybe simple, even if we
don't have conversions.
>>> But is it what people have problems with isn't it?
>>>
>> Some people are surprised by the implicit conversion, because they expect
>> it would not be there. the have this expectation because they do not
>> understand the conceptual model behind optional<T>.
>>
>> True, they have a problem. I claim, the root of the problem is not
>> understanding the tool they have.
>
> Should the tool match common user expectations (including novice
> users) or do you expect users to bend their models to the tools? ;)
>
>
>
Maybe the user needs a different tool. Boost or the standard can provide
it if there is a real need.
Of course having too much tools makes things more complex, but tools
that are less suitable to the users are used less or just not used.
Boost is a laboratory where tools can be experimented. std::experimental
also, but the cost to get accepted a proposal is very high.

As Nevin has said, if for the time to move something from
std::experimental to std we have two tools that do almost the same
thing, but with different interface, and we have no probes that one is
better than the other, there is a high risk that none goes to std::

Should the discussion wait until we have a std::optional in C++17 and
then propose a std::experimental::maybe (or whatever is called)?
I don't think we can wait. Some people is demanding now for these
alternative interfaces. If they are not in Boost or in the standard,
they will build its own libraries with minimal different flavors (there
already a lot of them).

Reassuming, I think that providing these alternative (less imperative
safe types) as safe_optional or maybe must be done together with a good
functional library including Functor, Applicative, Alternative, Monad.
This will take some time but I really think it is worth working on it,
at least in Boost.

C++14 is giving a lot of different design opportunities to those
available in C++98. People is experimenting just now.

I'm not sure we can have an acceptable solution with C++98. C++14 allows
a good experimentation, but I think that the complete solution will be
really there when C++ include sum types and pattern matching. Tis could
not be done for C++17 as there is not a proposal yet.

Best,
Vicente


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