[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Newsgroup Home]
|
[news.eclipse.modeling.mdt.ocl] Re: OCL closures implementation
|
Hi Christian
>
> Just $0.02 from a still-interested-though-somewhat-remote party :-)
Glad you're still here.
Your comments and an accidentally off-list discussion with Nicolas
Rouquette have helped me clarify my thoughts.
The practical implementation of closure is not closure; it is transitive
closure. closure would require some form of allInstances to discover a
domain over which to evaluate a predicate. So the new iterator should
probably be called transitiveClosure().
Whether transitive closure on objects or collections is more fundamental
is a chicken and egg argument. Both are useful and beauty is in the eye
of the beholder. The clinching argument to me for which is best is to
examine the lexical consequences. Imagine that both were available. Then
we have ample opportunity for
elems.transitiveClosure()
elems->transitiveClosure()
typos and confusion. Only one can be permitted.
If just object closure existed, the interaction with implicit collect
for collections would be nearly as confusing, so the collection wins for
lexical ergonomic reasons.
My more general definition using a helper routine is therefore:
-------------------------
The transitiveClosure iterator signature is very similar to iterate
transitiveClosure(<elem> : T, <acc> : C(T) = <acc-init> |
<expression-with-elem>)
where
<expression-with-elem> is an iterator body expression that may reference
<elem> and which returns a collection of type C(T).
<elem> is the name an optional iterator variable for use in
<expression-with-elem>
<acc> is the optional name of a result variable of type C(T)
<acc-init> is the optional initial value for <acc>.
The default result variable is an empty Set(T).
The invocation
roots->transitiveClosure(<elem> : T, <acc> : C(T) = <acc-init> |
<expression-with-elem>)
is equivalent to invocation of a helper routine as
roots->uniqueNameXxx(<acc-init>)
where the synthesized uniqueNameXxx is one of
Set::uniqueNameXxx(uniqueNameYyy : Set(T)) : Set(T) {
iterate(<elem> : T, acc : Set(T) = uniqueNameYyy |
if acc->includes(<elem>) then acc
else let candidates : Set(T) = <expression-with-elem>
in candidates->uniqueNameXxx(acc->including(<elem>))
endif)
}
OrderedSet::uniqueNameXxx(uniqueNameYyy : OrderedSet(T)) : OrderedSet(T) {
iterate(<elem> : T, acc : OrderedSet(T) = uniqueNameYyy |
if acc->includes(<elem>) then acc
else let candidates : OrderedSet(T) = <expression-with-elem>
in candidates->uniqueNameXxx(acc->append(<elem>))
endif)
}
Bag::uniqueNameXxx(uniqueNameYyy : Bag(T)) : Bag(T) {
iterate(<elem> : T, acc : Bag(T) = uniqueNameYyy |
if acc->includes(<elem>) then invalid
else let candidates : Bag(T) = <expression-with-elem>
in candidates->uniqueNameXxx(acc->including(<elem>))
endif)
}
Sequence::uniqueNameXxx(uniqueNameYyy : Sequence(T)) : Sequence(T) {
iterate(<elem> : T, acc : Sequence(T) = uniqueNameYyy |
if acc->includes(<elem>) then invalid
else let candidates : Sequence(T) = <expression-with-elem>
in candidates->uniqueNameXxx(acc->append(<elem>))
endif)
}
--------------------------------------------------
Introduction of the optional <acc-init> allows an 'object' iteration to
include itself in the initialiser for transitiveClosure of its elements.
Introduction of the optional <acc-init> allows the collection type to be
defined.
Support for OrderedSets provides a defined depth first ordering for such
transditiveClosures.
Support for Bags and Sequences is more dubious. The definitions above
give useful results for trees and invalid for graphs.
Regards
Ed Willink
Christian W. Damus wrote:
Hi, Ed,
Good point about the helper operation: I hadn't considered that the
formal definition of the closure() iterator could include additional
operations. Of course, it can!
I can think of a few arguments against the object iterator construct:
- it would be the first instance of its kind, a new language construct.
It probably needs to be able to show that it cannot be implemented
by any other mechanism (such as collection iterator) to justify that
- the "." operator makes "closure" look like a model operation. An "ocl"
prefix could help, but then it still looks like an operation, not an
iterator
- I'm not sure that it's a good idea to always include the initial object
in the closure. It's easy enough to extend the expression to add it
(e.g., "root->closure(kids)->including(root)" ) than it is to extend the
expression to remove it. In the latter case, e.g.,
"root->closure(kids)->excluding(root)" there's no way to know that
root shouldn't be in the closure because it was reached via a cycle
Note that the arrow operator coerces scalar values to singleton sets, so
you can do
root->closure(kids)
to get the closure of the kids association and, if necessary,
root->closure(kids)->including(kids)
to ensure that the result has the root, also. If this is a very common
case, it may justify an additional iterator, say
root->withClosure(kids)
that includes the starting collection.
Just $0.02 from a still-interested-though-somewhat-remote party :-)
Cheers,
Christian
On Sun, 2009-08-09 at 22:20 +0100, Ed Willink wrote:
Hi Neil, Christian, Nicolas
I stumbled over the experimental closure() implementation a few days
ago. It seems interesting. I see this has been raised as OMG Issue
13944. The RTF committee is so busy that if we want to get this in
OCL 2.3 there are better chances if the submission is formatted ready
for inclusion in the RTF report.
I think the recursive specification difficulty can be easily cured by
using infinite recursion of a helper function and a set that makes
further recursion redundant.
After trying to use the closure iterator, I think that an object
iterator form of closure is more fundamental than the collection
iterator. This allows the start object to be included in the results, so
a root and all its kids would be returned by
root.closure(it | it.kids)
whereas all the kids could be returned by
root.kids->forall(kid | kid.closure(it | it.kids))->asSet()
The collection iterator is nonetheless useful allowing the shorter
root.kids->closure(it | it.kids)
An object iterator is of course a new concept and so might bypass the
specification in terms of iterate ordanance.
Do you think it worth appending an update to Issue 13944 along these lines?
Regards
Ed Willink
---------------------------------------------------------------------
On Mon, 14 Jul 2008 15:59:16 -0400, Christian W. Damus wrote:
--------8<--------