blogPrivate features

dlebansais's picture

In Eiffel (to the best of my knowledge), a class B that inherits from a parent class A has access to all features of A. So far this hasn't bothered me at all, to the point that I often change my private functions in C# to protected.

But recently, as I was writing Eiffel programs to practice SCOOP, it occurred to me that I must now write a lot more features than I would before. Why is that? The reason comes from SCOOP controlled objects: what used to be simple qualified feature calls must now be turned into unqualified calls to 'private' routines. Let's see that with a quick example:

 

With SCOOP, if some_object is separate, then object_resource has to be separate as well, which means it must be controlled before we can get its value:

 

So far, so good. However, the requirement that objects must be controlled has the unintended effect of sometimes turning classes into a list of small, atomic features. This change does more than doubling the size of the source code, it also changes the design of the class by making a lot of internal calls available to descendants.

Let's take a look at {ENCODING}.convert_to:

 

If a_to_encoding is separate, then a new feature must be added to make a qualified call to a_to_encoding.code_page

 

My point is that, since the original version of ENCODING did not need this extra feature, there is probably no good reason to add it, from a design perspective. Therefore, SCOOP sort of forces you to design classes differently than what you would like to.

That would not be much of a problem (other than the obvious readability issue) if the new feature was private to the class, a possibility offered by several other languages but not in Eiffel. This sparked my curiosity, and I went through Object Oriented Software Construction, Eiffel, the language and Touch of Class. Alas, I couldn't find a good explanation of why to not allow features to be hidden to descendants. Can anyone come up with one?

Before SCOOP, if anyone wanted to expose a simple interface to clients but also to descendants, the option to write features made of big do...end blocks was there. This is no longer the case in the world of separate objects. It looks like this consequence was overlooked, and only the readability issue was acknowledged.

Comments

This was not overlooked but

manus_eiffel's picture

This was not overlooked but it was felt as a small consequence for having a safe concurrent model. Nevertheless we are currently discussing the possibility of removing that needs in some cases, and providing a way to do separate calls in line.

One example for the first case where it is not needed would be:


s: separate OBJECT

create s.make s.do_something


It is clear in the above case that the current processor is the only one with access to the `s' and thus there is no need to wrap the call for performing `s.do_something'.

For the second, we could have a way to make separate calls as in:


s: separate OBJECT ...

separate s.do_something


which has the effect that the call had if you put the wrapper. The concern some people are raising is that it lacks the wait condition. Indeed with a wrapper you could do require s.is_ready do s.do_something end, therefore you still need a wrapper if the wait condition is critical for the logic. In additon, one has to remember that consecutive calls such as :


separate s.do_something separate s.do_something


are not guaranteed to be contiguous, since another call to `s' can be inserted between the two. To have this guarantee you still have to go through the wrapper.

Currently I use

dlebansais's picture

Currently I use (credits to Jocelyn for reminding me about inline agents)

 

It's a bit verbose, but it does the job.

If people want some wait logic, then a wrapper is required too.

Syndicate content