Dear all,
After reflecting on this for the last month or so, I try to resolve it
with how I would do it in Eiffel. The first thing is that in normal
code, one should almost never have to check the type of something. It
does happen in very generic code, and we are bordering on that. If I
were to code access to some attributes via path in Eiffel, or in fact in
any language, I would do it like this:
-- assume we know that some_path is a valid path of some kind
-- assume also that we are talking about ordered containers (called
sequences in Eiffel)
if valid_container_path(some_path: String) then
obj_list := items_at_path(some_path) -- or container_at_path() would
be better
else
obj := item_at_path(some_path)
end
Now, you might think: I don't want to do that all the time. But most of
the time you won't have to - you will know in context of your code that
you are expecting a container, or a single item back, so you will
directly call the correct function. The above code is when your calling
code really has no idea.
But if we do it the other way, in Eiffel we would have to do:
obj_list ?= item_at_path(some_path) -- assignment attempt
if obj_list /= Void then
obj ?= item_at_path(some_path)
end
Even if we are in code that knows what it expects, you still have to do:
obj_list ?= item_at_path(some_path) -- expensive operation; not
statically checked at compile time
Or it could be done by checking types:
any_obj := item_at_path(some_path)
if conforms_to(dynamic_type(any_obj), some_list_type_id) then
obj_list ?= any_obj
...
as you can see, you can't escape doing an assignment attempt, which is
an expensive and non-type safe operation. There is an equivalent in C#.
So if you don't know the return type of the function, you always have to
do some check or test when you call it - every time.
Given that most of the time the code is most likely to know what kind of
thing the path it has is pointing to, it would be far better to have a
normal type-safe function to return that kind of thing, and not be doing
runtime type guessing.
This would mean using a set of functions like:
item_at_path (a_path: String): Locatable (or is it Any? Locatable means
the smallest thing you can get is an Element)
-- return whatever is found at path - could be a container or a
single Locatable
require
a_path_valid: a_path /= Void and then valid_item_path(a_path)
ensure
Result /= Void
container_at_path (a_path: String): Container<Any>
require
a_path_valid: a_path /= Void and then valid_container_path(a_path)
ensure
Result /= Void
valid_path (a_path: String): Boolean
-- True if path exists at all in structure
valid_item_path (a_path: String): Boolean
-- True if path refers to single attribute object
-- e.g some times paths like aaa/bbb, but also sometimes
aaa/bbb[xxx], if xxx resolves to single item
valid_container_path (a_path: String): Boolean
-- True if path refers to container object
-- e.g some times paths like aaa/bbb, but also sometimes
aaa/bbb[xxx] if nested containers
path_of_item (a_loc: Locatable): String
-- only works for Locatables, not containers
In the above, I deliberately avoided using 'List' rather than
'Container', since in theory a fairly high level type should be chosen.
In Eiffel I would use Sequence; in openEHR List might be alright - we
can decide on that later.
I also have not yet included a function that would return say a single
DV_DATE_TIME or Integer or whatever, as well as some complex types that
are not LOCATABLEs - and clearly paths into various parts of the model
can produce all such things. It may be that we want functions not for
'item' but for 'locatable', since these would be nice to use for some
kinds of path processing.
reactions?
- thomas
Heath Frankel wrote: