people[name=="Andrew"].friends ~= peter

Daniel Keep daniel.keep.lists at gmail.com
Sat May 6 21:55:42 PDT 2006



antonio wrote:
> 
> Daniel Keep escribió:
>> antonio wrote:
>>   
>>> As I introduced in http://www.dsource.org/forums/viewtopic.php?t=967
>>> object relations could be seen as hierarchycal structures.
>>>
>>> ¿Why not to introduce native syntax to "navigate into"/"Selec from" this
>>> kind of hierarchies?
>>>
>>>
>>> ex 1: Add Peter to the friends of people named Andrew and older than 18.
>>>
>>> Person peter = ...;
>>> Person[] people = ...;
>>>
>>> people[name=="Andrew" && age>18].friends ~= peter;
>>>
>>>
>>>     
> ....
>>> ---
>>>
>>> The main discussion about this idea was focused in 2 points:
>>> 1. "dot" or "not dot":
>>>     people[.married && .age>18]
>>>     vs
>>>     people[married && age>18]
>>>
>>>    "not dot" is more "D" syntax compliat (thanks to csaul).
>>>
>>> 2.Array syntax vs "Template" syntax:
>>>     people[married && age>18]
>>>     vs
>>>     people![married && age>18]
>>>
>>>   Personally, I prefer "Array syntax":
>>>     Person p = people[5];
>>>     Person[] p = people[3..5]; // 3..5 is a "condition"
>>>     Person[] p = people[5]; // ¿why not?
>>>     Person[] p = people[married];
>>>     Person[] p = people[age>18 && married];
>>>      
>>>     
>>>     
> ....
>> As I said, I agree with the idea: it's a very nice piece of syntactic
>> sugar.  The problem is with the syntax you've chosen.  One of D's
>> strengths is that the grammar is context-free, making it easy to implement.
>>
>> But, without knowing anything about context, what does this mean:
>>
>> 	people[i]
>>
>> Well, if "i" is an integer, then it's indexing the array.  If "i" is a
>> member of the elements of the people array, then it would be a
>> conditional expression.  But what if it's a member, and "i" is an
>> integer?  Is it a conditional or an index then?
>>   
> integer expresion refers always to the index.  a char[] expresion refers
> to a key (associative array), etc...

Out of interest, how do you resolve the following:

	char[bool] stuff;

In that case, how do you tell between a key and a filter expression?

>> And what if both are defined?
>>   
> class Person {
>     public in i;  
> }
> void main( ) {
>     int i;
>     Person p = new Person();
> 
>     with( p ) {
>        i = 5; // property of p
>        .i = 3; // dot signifies: one level out of this scope... the int
> i declared one.
>     }
> }
> 
> then
> 
> people[i] for member
> people[.i] for out of scope declared integer
> 

I suppose that works... but what if the person writing the code doesn't
realize his local variable is being shadowed by the object's member?

>> Even if the compiler can work out a way to distinguish this, the syntax
>> in its current form looks very hard for humans to parse in certain
>> fringe cases.
>>
>> As you said, an alternative is "templateish" syntax:
>>
>> 	people![i]
>>
>> I like this more, since it's *explicit* about what's going on.  That "!"
>> means "Hey, don't actually index the array; select elements of it only".
>>   
> Yes... I think explicit yntax is really more clean:: *you convinced to
> me :-)*

Huzzah :)

> Let's go with the new proposed syntax:
> 
> *STEP 0*:  drinking from oter sources... XPATH syntax...
> 
> xmlNode.SelectNodes("tagNameA[conditionA]/tagNameB[conditionB]")...
> 
>     * "[condition]" signifies: evaluate this condition on left side
>       tagNode contents.
> 
>     * When no condition is imposed XPath assumes "[true]";
> 
> then  xmlNode.SelectNodes("*tagNameA/tagNameB[conditionB]*") is
> equivalent to
> xmlNode.SelectNodes("*tagNameA[true]/tagNameB[conditionB]*")...
> 
> 
> *STEP 1*:  lets propose something:
> 
> The syntax used by the XPath D expression must be "autodefined", becuse
> whe dont want to use the "xmlNode.SelectNodes( ... )" method :-p.  One
> solution is using specific *![Condition]* that defines "this is an XPath
> condition"...
> ..
> 
> *AggregateExpression*![*condition*] signifies: evaluate the condition on
> left side aggregated elements and build an aggregated result with
> elements that passed condition (the result could be a dynamic array)
> 

Just one point I'd like to make: AggregateExpression may not neccecarily
be an array.  It's possible that it is, say, a very *very* large
iterable object.  The syntax so far is fine, but I think user defined
classes should be given the option of changing what the result of this
is, or even specifying that the result should be an iterable object.

> *STEP 2* : what to do with not aggregate expressions :-(
> *
> ex:
> *Person[] youngGrandmothers =  people![childs.length!=0].*mother![age<36]*
> 
> *NotAggregateExpression*![*condition*] signifies: evaluate the condition
> on left side Element and build a dynamic result array with 0 or 1
> elements (depending on the condition evaluation result).
> 

What if you did this instead:

        people![childs.length!=0].mother![age<36]
   ==>  ((people![childs.length!=0]).mother)![age<36]

In this case, let it be that:

* "people![childs.length!=0]" is an aggregate result,
* "(...).mother" is an aggregate containing all the values of "mother"
from each of the elements in the previous result (ie: a new aggregate
result).  Obviously, you can't do this with a regular array...
* "(...)![age<36]" is yet another aggregate result which selects the
appropriate elements from the previous result.

Basically, these "aggregate results" would behave kinda sorta like
arrays and kinda sorta like iterables without strictly being either.
Kinda wishy washy, I know :)

> *STEP 3:* whe have to use ![] in all hierarchy node:
> ex:
> 
>     // whe can asume than *![] is equivalent to ![true]*
>     countries![population>10000000].people![age<3].mother![].mainHome![].rooms![windows>2] 
>     **

If you take the above suggestion of not using "real" arrays for the
intermediates, then you don't need to specify ![] at each level.

> some exceptions: the last hierarchy node doesn't need to be followed by
> the ![] in some cases:
> 
>     * When the node is a Method:
> 
>         ex:
>             people![married].*doSomething();*

Hmm... not sure if I'm comfortable with that.  Selecting data is fine,
but then performing an operation on that...  The problem is that
everywhere else in D, this would be a *single* function call.  In this
one particular case, it's multiple function calls.

If you wanted to do this, I think it might be better to spell it out
explicitly:

	foreach(person ; people![married])
		person.doSomething()

>From the Zen of Python:  "Explicit is better than implicit."

> **
> 
>     * When the node is a property and it's on the left side of an
>       assignment
> 
>         ex:
>             people![married]*.name = "Peter";*
>             people![married]*.childs ~= new Person();*
>             people![birdtha=today]*.age++; *// This introduce an
>         implicit right side assignment property evaluation... I suppouse
>         this is an "exception" because compiler can solve this easily.

Looks handy :)

> *STEP 4: *right side must be a member.
> 
> Expression![condition].*member*

I think the "must be a member" is a bit strict.  Currently, you can do
things like this:

	char[] firstFive(char[] a)
	{
	    return a[0..5];
	}
	
	auto firstFiveLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".firstFive;

I'd like to be able to still do that.  Perhaps just saying "use whatever
lookup policy D currently uses on arrays" would be sufficient.

> **
> *STEP 5:* How compiler expands this expression.
> 
> I suppouse Walter must decide between a preorder or inorder evaluation
> 
>     * preorder:  first all parents, then their childs :
> 
>              A![].B![].C![]
>              could be evaluated like this:
>              foreach(a in A)
>                 tmpA~=a;
>              foreach(a in tmpA) foreach(b in a.B)
>                 tmpB~=b;
>              foreach(b in tmpB) foreach(c in b.C)
>                 tmpC~=c
> 
>     * inorder: first parent, then their chids... when childs evaluated
>       next parent... and so on
> 
>              A![].B![].C![]
>             could be evaluated like this
>              foreach(a in A)
>                 foreach(b in a.B)
>                    foreach (c in b.C)

I would say that in order would be best... provided it's implemented as
chained iterators.  In other words, each of the intermediate "aggregate
results" should only generate elements as neccecary.

The reason for this is that then you can perform very complex filters on
large data sets.  If you did it "preorder", then this would be
ludicrously expensive:

	SomeHugeDataSet![size > 50*Megabytes]
		.largeInternalObject![size > 75*Megabytes]

> Well... I'm not an expert, but how hierarchy is evaluated is a compiler
> work and programmer must be isolated about the compiler solution.
> 
> We can assume than *result elements order can't be predicted* (like
> realtional model).  Results needs to be postprocessed (distinct, sort,
> ...) if needed.
> 
>> At any rate, nice proposal, and I look forward to seeing something come
>> of it :)
>>
>>   
> I agree... this is, basically, my dream:  People writes constantly FOR +
> IF structures  that can be easily expressed with this propossal.
>> Oh, one other thing that suddenly occured to me: what if "people" isn't
>> an array?  You use 'foreach' in your expansions, but what if "people"
>> CAN be iterated over, but isn't an array in of itself?  Then the syntax
>> becomes downright misleading!
>>   
> D propose an standard implementation for "aggregate" classes... the main
> goal now is to propose something D compatible:
> 
>     * Is there a standard I_Iterable interface?
>     * foreach( ) recognizes this I_Iterable interface?
>     * Array acomplish with the I_Iterable interface?
> 
> I_Iterable is not part of D programming Language.... and this is another
> discussion :-):
> 
>     * It could be perfect some standard Interfaces recognized by the
>       compiler, like c# foreach or using staments (IIterable and
>       IDisposable interfaces).

I've always thought it would be nice to have a few "standard" interfaces
attached to things like arrays.  Only thing is that I imagine converting
between, say, char[] and IIterable!(char) would be very expensive.

Again, I like where this proposal is trying to go.  One question,
though: have you looked at Linq in C#?  I think it's slated for version
3.0, but it looks quite similar to what you're proposing, and allows you
to do selects and transforms.  I'd give you an example, but I can't
remember any :P

	-- Daniel

-- 

v1sw5+8Yhw5ln4+5pr6OFma8u6+7Lw4Tm6+7l6+7D
a2Xs3MSr2e4/6+7t4TNSMb6HTOp5en5g6RAHCP    http://hackerkey.com/



More information about the Digitalmars-d mailing list