Boost logo

Boost :

From: Hartmut Kaiser (hartmut.kaiser_at_[hidden])
Date: 2008-06-01 14:43:35


> > I'm perfectly aware of this. And actually I expressed exactly the
> same
> > by pointing out the main difference between the two sets of
> operators:
> > the logical operators I proposed are for the futures themselves while
> > the others are related to the future results.
> >
> > But you're diligently ignoring my point: overloading the operators
> for
> > the future results is conceptually equivalent to having a default
> > conversion operator to the result type for the future itself! And for
> > that reason I would not like to see something like that in the future
> > library.
> Ah, I think I'm understanding you better now. You are appalled by the
> idea of treating a future<T> like it was a T, is that correct?

Yep. That's the separation of concerns I was alluding to. Futures should do
one thing, and that is encapsulating deferred or eager execution of a
(remote) task.

> You
> want a future<T> to only be treated like an abstract handle to a value,
> not the value itself? I wasn't ignoring your point, it's just that
> your view is extremely foreign to how I view futures, so your words
> were almost meaningless to me. I view being able to view a future<T>
> as a placeholder for a T object as a large part of what makes futures
> useful. I implemented the future class in libpoet to support that,
> with implicit conversions from future<T> to future<U>, from T to
> future<T>, etc. And being able to treat a future<T> as a placeholder
> for a T is essential to implementing things like poet::active_function.
> I don't particularly like the implicit conversion from T to future<T>,
> but that has absolutely nothing to do with not wanting to treat a
> future<T> as a placeholder.

What's the difference between treating a future as a placeholder and having
a future implicitly convertibly to its result type? Doesn't a placeholder
imply having this implicit conversion?

> It's simply because the conversion can
> block, and thus produce unexpected behavior (an object appearing on the
> right hand side of an assignment stalling your program). The explicit
> future::get() at least gives a hint that something more than a quick
> conversion might be happening.
> I guess the features that allow a future to act as a placeholder could
> be split out into a separate higher level class, call it
> "future_value", which would internally contain a plain old future. The
> idea doesn't really do anything for me, but maybe you would find it
> preferable?

I could live with that. The future_value would be implemented on top of
anything encapsulating deferred/eager remote execution and its sole purpose
is to provide implicit conversion, function argument lifting, etc. No mixing
of concerns here, fine.

> > OTOH, IMHO, having the logical operators alone improves code
> > readability, simplifies the implementation (by avoiding multiple
> > overloads for different parameter counts), allows to build more
> > complex logical execution restrictions as (f1 || f2) && f3 (which is
> > not possible using the proposed wait_for_all and wait_for_any), and
> > all that without having to sacrifice any of the existing
> functionality.
> The
> poet::future_barrier/future_composing_barrier/future_select/future_sele
> ctor
> stuff I'm currently working on allows for composition. Did you see the
> earlier discussion on this a couple weeks ago? For example this
> thread:

I didn't follow this discussion, thanks for the link.

> > > Personally, I don't particularly want to see a future library
> > > overload any logical operators at all, or at least have the
> > > overloads sequestered in separate headers that aren't included by
> > > any of the other future library headers.
> >
> > That's a matter of taste, for sure. But would you mind telling us why
> > you don't want to have these? Your opinion sounds to be quite strong,
> > so you must have good reasons!
> It's the obvious reason. I don't think taking a function with a 2
> letter name, which is already overloaded, and adding a new set of
> overloads to it which have semantics completely unrelated to the
> existing overloads is a desirable or aesthetically pleasing interface.
> No one would even consider doing such a thing if the function wasn't an
> operator. I guess I just don't find operator syntax as compelling a
> feature as others.

Come on. You and I _know_, that an operator isn't just a function with a two
letter name (and as you admitted in a later mail, they aren't yet
overloaded, so your reasoning isn't really convincing). An operator is
certainly more than that, i.e. it provides infix notation, well defined
associativity and precedence rules, terse expression syntax, etc.

The semantics might be unusual, I agree. But the same was true when Bjarne
introduced the shift operators for input/output. Nowadays nobody even
discusses this anymore, it's just 'natural'. Once you think about it for a
while it's very 'natural' as well to use || and && to express logical
execution restrictions for futures - not their values, giving you a very
powerful means of controlling fine grained parallelization.

BTW, we could go even further by overloading '->', '>>', or '>>=' to express
dependencies between futures (except that >>= probably has the wrong
precedence for that, just a hunch, I didn't check). This would be just
another means of controlling parallelization while using an easy
understandable syntax. But I'm not going to propose _that_ :-P

And just compare the amount of code needed to write something equivalent to
(f1 || f2) && f3 using the proposed API from the link you gave me above.
That's really appalling - certainly only IMHO!

Regards Hartmut

Boost list run by bdawes at, gregod at, cpdaniel at, john at