ETCollection higher-order messaging for merging into stable
Updated 5 months, 1 week ago
| Niels Grewe | Reviewers | ||
| EtoileCore | |||
| None | Étoilé Stable | ||
Part of the higher-order messaging functionality has already been reviewed (http://review.etoileos.com/r/102/) This changeset includes the base functionality and further improvements: * Many, many patches by Quentin (thanks a lot!) * The ability to filter collections based on the attributes of their contents * ETEachProxy to iterate over contents of collections used as arguments to a message (This depends on a receiver that knows how to handle this, currently only map and filter proxies do) * Unit tests for the stuff that uses blocks.
Extensive unit tests included.
Posted 9 months, 3 weeks ago (November 16th, 2009, 9:15 a.m.)
Looks good and the new features are just great! I added various small comments. The main one is about to unify the naming between 'inject' and 'fold' API. Is there a particular reason why you wrote the new 'recursive' functions that work in tandem with the each proxy in a recursive style? I don't suggest to rewrite them, but I was just curious to know because I would have tried to write them in a non-recursive style to make them easier to understand. I haven't thought really hard whether that's possible or not. The spacing is a little bit inconsistent in some places, it doesn't really matter though. For future code, I suggest to write function calls and macros with arguments like that: f(a, b, c); and methods as below: - (void) blabla: (id)x bla: (id)z and [self blabla: y bla: x]; More examples are visible in the coding style page. The test suite runs fine for me on both GNUstep and Mac OS X.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
The note doesn't sound really clear to me. May be add an example to illustrate the point.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
A small example would be nice.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
A small example would be nice.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
A small example would be nice.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
A small example would be nice.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 1) -
For consistency purpose, I would suggest to unify the naming of all the folding methods. e.g. Rename -injectXXX: methods to: -leftFoldWithInitialValue:intoBlock: -rightFoldWithInitialValue:intoBlock: The alternative is to rename the 'fold' HOM methods with 'inject'. Then -injectXXX methods could be removed and -inject:into: added to a Smalltalk-specific framework. -inject:into: would just call -leftFoldWithInitialValue:intoBlock: or its associated HOM function.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject may be.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject may be
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
From what I understand the first two if of the method are only executed the first time the method gets called. So why not move them in -initWithOriginal:? If that's not possible I would move this if block into the previous one and keep it separated from the rest with a blank line like that: if (nil == contents) { contents = if (nil == contentsEnum) { contentsEnum = } } id object = -
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
You can replace the two lines with DESTROY(contentEnum);
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject may be
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
I'd add a comment like: /* No method arguments (only self and _cmd as invisible arguments) */ and/or use a boolean variable like that: BOOL isUnaryMessage = (argCount < 3); if (isUnaryMessage) { return; } To also put a blank before and after this if statement would be better. -
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
; i++
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Move this comment before the 'if', otherwise we get the impression it applies to the code that follows.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Selector: @selector(
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
I have never commented on that since it isn't really critical but I suggest to use spaces around all binary operators (except '.' and '->') as explained in the C operators section of the coding style page. The reasoning behind that is to make obvious that unary operators (and also '.' '->') have higher precedence than binary operators without putting parenthesis around them. For unary operators, putting parenthesis remains necessary when the precedence order between them isn't clear.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
= (slotField.fields[index] | 1 << i%8); and I think I would rather write: = (slotField.fields[index] | (1 << (i % 8)));
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Am I missing something or is the same than: slotField.fields[0] = 1;
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Wouldn't a comment as the one below be clearer? // the slots to be set as the invocation arguments otherwise we get the impression 'slots' data structure itself has to be filled.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
I would turn the next two loop into two inline functions to obtained something like that: findFirstSlotIDWithEachProxy(slotID); NSUInteger nextSlotID = slotID + 1; findFirstSlotIDWithEachProxy(nextSlotID);
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
nextSlotID to be consistent
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Shouldn't this variable rather be named 'eachProxy'?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
A constant such as LastSlotID should be defined for 127.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
while (
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Remove the trailing semicolon. And I would rather say: // Set the present argument I'm not really sure what a 'slot' means precisely. Is a slot exactly the same thing than an argument or is it the value/object identified by a slot id marked with '1'? That's why I suggest this change.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Similarly to my previous comment, I suggest something as below. Although you might have a better way to express all that... /* If there are no more arguments to be set, the invocation is ready to be invoked, otherwise more slots remains to be set as arguments. */
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
You could make a bit more explicit with: BOOL isFistTime = (0 == count); if ((NULL != ctx->elementHandler) && isFirstTime) { } else { if (ctxt->modifiesSelf && isFirstTime) } -
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Wrap on two or three lines with a tab at the beginning or align the arguments vertically in a better way.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
(ctxt->modifiesSelf && (0 == count))
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
'when we run for the next target' doesn't sound really clear to me. May be something like: when we invoke the invocation on the next target provided by 'levelProxy'
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Same as previously
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Should be named nextSlotID I think
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
If this is an ETEachProxy instance, I'd rename it eachProxy otherwise we get the feeling there is another kind of proxy involved.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
while (
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Same as previously
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
result = (result || (BOOL)filterResult);
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
result = (result || recursiveFilterWithInvocation(inv, slots, nextSlot));
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETMutableCollectionObject
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
&& NO == isArrayTarget // to be consistent within the function
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
if (useBlock)
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Add a small comment or may be a function: MarkSlotsAsEmpty(eachedSlots); or NewSlots(); that returns an initialized slotField_t structure
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
You could do: BOOL usesEachProxy = (eachedSlots.fields[0] & 1); if (usesEachProxy) { } else { } Would be nice to have one less statement nesting too, but I don't have any suggestion to eliminate a level. -
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
The alignment of the arguments is weird since they don't represent pairs or something like that. I suggest to just wrap on several lines.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
if (useBlock)
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
FOREACHE(content, element, id, contentEnumerator)
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
if (NO == shallInvert) // to be consistent with the rest of the function
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Is this casting really needed?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
See previously in ETHOMMapCollectionWithBlockOrInvocationToTargetAsArray
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
See previously in ETHOMMapCollectionWithBlockOrInvocationToTargetAsArray
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Indent by wrapping with a tab at the beginning.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
if (useBlock)
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
contentsFirst, firstObject
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Missing tab
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETMutableCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
id<ETCollectionObject>?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Merge with previous line.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Missing spaces after each comma.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Merge with previous line.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Missing spaces
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Better to autorelease on the line where the instance is created
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
That's really minor... It's better to use a more descriptive variable name such as 'should/shallInvert' than 'aFlag' Apple does it a lot, so I took the habit to do it in my own code, but I don't think it's a good idea in the end. It doesn't matter that much when the methods are small, but when the method size grows a bit, it's better to get rid of it.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
id<ETMutableCollectionObject>?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
id<ETMutableCollectionObject>?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
id<ETCollectionObject>?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETMutableCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
ETMutableCollectionObject?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
Missing space
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revision 1) -
for (int i = 0; i < [xxx]; i++)
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Use ASSIGN
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Use ASSIGN
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Insert blank line.
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Should rather be: UKObjectsEqual(@"letters: foobar",[[inputArray leftFold] <tab>stringByAppendingString:
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
UKTrue([folded isEqual: @"foobar"] || [folded isEqual: @"barfoo"]);
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
UKTrue([folded isEqual: @"foofoobar"] || [folded isEqual: @"barfoofoo"] || [folded isEqual: @"foobarfoo"]); or UKTrue([S(@"foofoobar", @"barfoofoo", @"foobarfoo") containsObject: folded]);
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
This line is really long. It is not ideal, but you could move and indent stringByAppendingString: on the next line.
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
This line is really long. You could do: NSMutableDictionary *first = [NSMutableDictionary dictionaryWithObjectsAndKeys: @"foo",@"one",@"FOO",@"two",@"Foo",@"three",nil];
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Wrap on several lines with a tab at the beginning.
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
Missing blank line
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
I would use a much simpler wrapping style: NSMutableArray *expected = [NSMutableArray arrayWithObjects: @"fooFooFOO", @"fooFooBAR", @"fooBarFOO", @"fooBarBAR", @"barFooFOO", @"barFooBAR", @"barBarFOO", @"barBarBAR", nil];
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
May be add a blank line in other test methods as you have done in this one to separate test assertions a bit.
-
Frameworks/EtoileFoundation/Source/TestETCollectionHOM.m (Diff revision 1) -
The note would be better placed at the beginning of the file. What do you mean by 'remaining collection classes'? May be rewrite the last sentence to be clearer.
Review request changed
Updated 9 months, 3 weeks ago (November 19th, 2009, 12:52 a.m.)
-
- added Diff r2
Update to reflect the results of Quentin's review.
Here is the review of the last diff. Everything looks ok to me. There is still the -[NSDictionary identifierAtIndex:] issue, but I haven't figured out how we should handle that cleanly. From an EtoileUI perspective, the problem is roughly… How do we generate a key when an object is put in a dictionary by drag and drop? Should we reuse keys when objects are moved across dictionaries? May be only if the two dictionaries serve the same purpose? This last point probably only makes sense when we have two model objects which uses a dictionary as their storage. In this case, we can check their type/class to know their purpose. Another thing I was wondering about… Is it ok to use -mappedCollection in the same way than -do in Smalltalk. For example: [[views mappedCollection] display]; iirc, there are cases where this doesn't work. I don't recall exactly which ones. May be when the argument message return type is a BOOL or void. This use case is very common and -mappedCollection doesn't express the intent very well, a more explicit -do message could be nice. What do you think?
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revisions 1 - 2) -
int index = 1 / 8; without parenthesis would be fine since it's a simple arithmetic expression.
-
Frameworks/EtoileFoundation/Source/ETCollection+HOM.m (Diff revisions 1 - 2) -
to be replaced <-- missing 'd'
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
When you mention a method, you can write -methodName. By adding a minus or plus sign at the beginning of the method name, autogsdoc automatically generates a link when the method belongs to the same class than the documentation. Cross-class references can be done with -[OtherClass methodName] iirc.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
Insert a newline here to have the note in a separate paragraph. Also may be it is worth to explicitly state that: [anObject addThing: [things each]] won't do anything. I tried to use this pattern several times since it feels natural :-)
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
I suggest a newline here too
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
I suggest a newline here too
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
I suggest a newline here too
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
'as its first argument' to know the thing to which 'first' applies to? Not really sure about that since English is not my native tongue.
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
Same remark… 'as its second argument'
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
An example would be nice
-
Frameworks/EtoileFoundation/Headers/ETCollection+HOM.h (Diff revision 2) -
May be it's overkill, but -mapInfo could be declared in an informal protocol