Marshall Schor commented on UIMA-1524:
Thanks, Richard, for your thoughtful dialog on these topics. It's nice to find
someone else interested in the arcana of principled API design :-).
1) re: Fluctuating between positional-style and keyword style: the keyword
style is the most "factored" style - lets one build what you want with a
"minimal" set of keys. So it's useful to list these as separate things (as
done in the diagram), to see the totality of what one might reasonably "build"
out of a minimal set of primitives, and what those primitives might be.
But the keyword style is verbose, so it seems good design to support positional
for frequently-used idioms, for more concise/compact code.
2) Yes, your understanding of the Gliffy diagram is correct re: the object
without a "terminal" is something supporting special methods for UIMA and also
the stream interface. My thought is that when you use a stream method, that
will result in the creation of a Stream object, based on everything you've
specified, up to that point, and from then on, it will act like a stream.
This "automatic" conversion to stream is a "convenience"; we could dispense
with it, and require the user to explicitly write ....x().y().stream().a().b().
where x, y are the uima specific methods, and a, b are stream methods. It
seems better (more concise/compact) to leave out the stream(), though. Maybe
it could be optional, and the documentation (for learning) could start with it
explicitly present, and only later drop it; this could demystify the API
3) It seems right that having more than one "type" spec is more likely an
error, than a useful feature, so I agree it would be more helpful to throw an
exception if that happened.
4) Because "type" is used frequently, I think it's a good candidate to allow it
as a positional argument.
5) When moving keyword arguments into positional ones, should we drop the
keyword form? I'm a bit on the fence. Sometimes I think users prefer an API
style which lists arguments with their keys (as in the form type(aTypeSpec),
uniformly, even if there are positional forms. I don't think there's a cost to
keeping this option, so I'm on the side of keeping it.
**Decision requirement::** dropping the *type* call - I'd say to keep it;
throwing an exception if called twice: I'd say yes (because it's more likely an
error than a wanted feature).
6) multiple location requirements. I agree it would be nice, but it might be
difficult to (efficiently) implement. People might write forms like
within(span1).within(span2), where the spans had some overlap. It seems we'd
have to go through and figure out the semantics for lots of combinations of
method (keyword) chains... So maybe we could not do this in version 1, and
later figure out some often requested instances of this as a version 2
These are perhaps "tricky". In the example of
.within(sentence).following(predicateVerb), even though this sounds sensible, I
think it's somewhat misleading. The following(predicateVerb) completely
specifies the position, and the .within(sentence) just serves as a boolean
switch - is the predicateVerb location is within(sentence), I think this is a
confusing way to represent this versus something like:
or something similar that does boolean operations on spans (with or without
**Decision requirement:** allowing multiple location specs - I would severely
restrict this in version 1 to just those easily supported by the current
indexes/iterators, which in my mind would be just one of the
covered/covering/moveTo(fs) kind, and one displacement kind; the semantics
would be that the displacement operation follows the other.
7) I like "displacement" instead of "offset", I'd like even better a shorter
word :-) ; some possibilities: shift, shifted, shiftedBy, move, moved, movedBy
(the "ed" forms are more declarative, but sound a bit clumsy to me). Of
these, my choice is "shifted": e.g.
cas.select(Token.type).within(fs).shifted(3), but not a strong preference.
8) Semantics of displacement: I had not thought of the complexity of having it
operate with respect to some other index/type; I thought of it as moving the
point where you start by iterating moveToNext or moveToPrevious. Our current
mechanisms support this trivially, I think. Also, in the form
"cas.select(Token.type).following(predicateVerb)", the "type" of predicateVerb
(surprised) need not even be a subtype of Token.
**Decision requirement::** displacement meaning: I'd prefer making it just an
interated MoveToNext or Previous.
9) Re: supporting enhanced for loop: I agree it would be good to support this.
I think the implementation could do this, so your example would work. It would
switch to the "stream" alternative only if some stream operation was specified.
> JFSIndexRepository should be enhanced with new generic methods
> Key: UIMA-1524
> URL: https://issues.apache.org/jira/browse/UIMA-1524
> Project: UIMA
> Issue Type: Improvement
> Components: Core Java Framework
> Affects Versions: 2.3
> Reporter: Joern Kottmann
> Existing methods should be overloaded with an additional Class argument to
> specify the exact return type. This changes make down casting of returned
> objects unnecessary.
This message was sent by Atlassian JIRA