Boost logo

Boost :

Subject: Re: [boost] [castor] Interest in Logic Paradigm for C++ ?
From: Roshan (roshan_naik_at_[hidden])
Date: 2010-05-06 15:14:37


On Wed, 05 May 2010 09:10:25 -0600, Zach Laine
<whatwasthataddress_at_[hidden]> wrote:

>
> I understand that, and I'm not trying to get you to change the 0 <= N
> results semantics. I'm just indicating that the canonical way to
> communicate that in C++ is with a pair of iterators.
>

> Having to use a nonstandard
> idiom for iteration to get the functionality of your library is what
> I'm objecting to.

True.. its is canonical to traditional C++ given the existing paradigm mix.
Iterators were designed as the "glue" between
algorithms/containers/streams.
However in LP, all three of them are represented by relations... so
iterators are
are not that useful.

Initially (few years back) I pursued some iterator based designs (among
others) which fell apart very quickly.
I only recall they didn't fit in very well. All the details escape me
now.. But the core idea of
being able to dereference an iterator to get a *single* value is at odds
with mode of operation here.
Coroutines eventually emerged as the more appropriate solution.

Introducing iterators based generation of solutions also gives the false
impression that this could
somehow be used in conjunction with STL's iterator based algorithms... as
the *iterator would only produce true/false
regardless of the relation.

Your interest in iterators seems to be stemming from the desire to use a
for each (or range based for loop)
like construct to go over all solutions. These are designed to simplify
working with iterators... I feel it is more
appropriate to extend these looping mechanisms to accomodate coroutines
rather than the other way around.

> I think you might misunderstand me. I'm pointing out that, as a
> reader of some code who happens not to be that code's author, I'd like
> to be able to inspect the code and predict to a reasonable degree what
> the code does. Knowing whether "while( franksChildren() ) {...}" will
> *evaluate* to <= 1 result, vs. <= N results, is significant.
[..snip..]
> It is
> the same semantic difference as find() vs. find_all().

Ah! When you say find().. you are explicitly requesting first result (even
if there is more than one) and when you
say find_all() you want all results. In LP too it is explicit :

First result only (even if there are more):
  if(franksChildren())
    ...

All results:
  while(franksChildren())
    ...

> There's no fundamental reason why you should be limited by the use of
> iterators. The iterator constructor invoked for the begin() iterator
> evaluates the relation once (i.e. it is very much like
> relation::operator()). If the evaluation succeeded, operator*() will
> return the result; otherwise, the begin() iterator evaluates as equal
> to the end() iterator, indicating that the sequence is empty, and that
> it is unsafe to either dereference or increment the iterator.
> operator++() simply repeats this process. Am I missing something?

I will need to see a more fleshed out design to comment on this. And
see how some of the existing samples in the tutorial would look. This is
deeply ingrained decision and not an orthogonal one. BTW how can operator
* on the
iterator by used to access the multiple in/out parameters ?

Too time consuming to list all issues. One of them ... It is likely to
imply that when
relations are defined imperatively as coroutines, they will be required to
define custom
iterator objects... which are a huge pain (way too many members!).

Also the upcoming lambda support will complement the coroutine style
definitions nicely. It is much
easier to imperatively define a relation/coroutine using lambda as
compared to
iterator objects...

// psuedocode
relation blah(x,y) {
   return [=] { .. ; yield true; ..; }
}

>> relation result; // must be initialized
>>
>> As a relation must be defined. But this is semantically different:

> Is there a specific reason for that? Is there a reason why
> relation()() can't just return false? If so, why not default
> construct relation the same way, just as Disjunctions initializes its
> clauses member with False()?

Conceptually, the three classes Dis/Con/ExDis-junctions correspond to
con/dis/exdis-junctive
forms of clause definitions. The type relation has no such association, it
is totally free form.

The type relation has no such affiliation. So with the three classes,
performing += is more naturally suggestive of
the implied precedences of the internal operators within the final
expression after a series
of += (i.e. push_back) operations. I chose not build an implicit semantics
at the lowest level concept of type relations.

If relation is defaulted to False(). Then realtion&=blah() ... would be
False()&&blah(). Which is a problem.
But with relation|=blah would be False()||blah() which is not a problem.
They could also be just blah(). But it is not
distinguishable if relation was default initialized to False or explicitly
initialized to False() ... only in the latter
case you want to preserve it. I think there were some minor efficiency
issues as well... which will take me some effort to recall now.

I felt it was not a great idea to build any intelligence into such a low
level concept as type relation.
That said, i think this still an open case.... just needs to more time to
actually experience the trade offs.
I think it was good to be conservative decision for an early design stage.
It easy to add this into relation .. but removing
is near impossible.

> Why is it too late?

Compatibility and lack of significant real benefits.
The precedence benefits are only if your clauses are in disjunctive normal
form. Not otherwise.
I have been recently considering having both && and &. Also || and |.
As they both are useful in different situations.

> Don't get me wrong -- I like the use of operator^(), as I said. Just
> call it something else. :)

Suggest a better name :) it might happen.

>> That leads to overload resolution issues and also I prefer eq_f separate
>> from eq for clarity of semantics. Though I am thinking of collapsing
>> eq_f
>> and eq_mf into eq_f with some bind mechanism. Boost bind doesn't do the
>> trick as it will not automatically dereference the lrefs involved at the
>> time of evaluation. These were primarily provided to improve readability
>> over the equivalent syntax involving bind.
>
> Ah. What was the overload resolution problem?

If the obj has a member bool operator()() defined... we cannot say
whether eq_f semantics or eq semantics should be applied.
It is possible today to apply eq() semantics to objects that have
operator().

> You wrote this in your Design and Implementation doc:
>
> "Early on, support for creating relations directly out of a boolean ILE
> expressions without need for an “adapter” relation like predicate was
> implemented, but this facility was later withdrawn as it ran into several
> problems including some limitations imposed by C++. The current design
> still leaves the door open to revisit this facility in the future
> (perhaps in
> C++0x) with an eye towards preserving compatibility."
>
>> predicate is an adapter relation used to turn bool
>> functions/function objects into relations which can then be combined
>> with
>> other relations. You cannot be use them directly.
>
> Right. And I'm asking why not. I'm mainly just curious.

Oh. An ILE like (i==j) is function obj that returns bool and takes no
args. Thus it type erases to relation.
But it isn't a relation as it is a subroutine and not a coroutine. No
general way to determine if its a
truly a relation.

> As an aside, I can write this:
>
> lref<int> c; // <- Oops!
> relation franksChildren = father("Frank", c);
>
> and I get this error:
>
> includes/refcountedptr.h:98: error: no matching function for call to
> std::basic_string<char, std::char_traits<char>, std::allocator<char>
>> ::basic_string(const castor::lref<int>&)
>
> It would be nice if it were impossible to mix these due to
> BOOST_STATIC_ASSERT/static_assert enforcement of type in lref<>, which
> would provide better feedback.

In this case the best place is to probably place the assertion inside
father itself. Isn't it ?

Boy... I am getting a rigorous mental workout answering all your
insightful probing questions.
:)

-Roshan


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