Dear Alexey,
> class A end
> class B inherit A end
....
> a: expanded A
> b: expanded B
> ...
> a:= b -- validity rule violation - why? What is wrong with polymorphism for expanded objects?
A class is not just a record type. It comes from an abstract data type, with semantic properties. Were the assignment permitted, you would want it (as I understand) to copy just the common fields. But assignment of objects is not field-by-field copy. It is only that by default (standard_copy). It applies `copy’, which every class can redefine to yield specific copy semantics fine-tuned to the needs of the class. Which version of `copy’ do you want to apply, A’s or B’s? Presumably the A version. But that does not work.
First, `copy’ requires the source and target to have identical (not just conforming) types. In fact standard_copy has exactly the same requirements. So the assignment, were it to be permitted, would need special rules. Change the semantics of assignment for just a very special case? I don’t think anyone would vote for that.
This requirement of identical types is there for good reasons but assume we drop it. The rule still doesn’t work. There are many catastrophe scenarios; here is just one. Assume a secret (non-exported) integer attribute xx in A. A also has a function x that returns xx, and a procedure set_x that assigns to xx. The procedure requires a positive argument and the function ensures a positive result. All fine and consistent. In B we always use 0 for xx, we add an attribute yy, and we change the implementation of x and xx so that they work on yy rather than xx., keeping their original properties (positive argument, positive result). Semantically this is fine, the rules are respected. But if we copy the xx field from an instance of B to an instance of A the resulting xx field has value 0 and x returns the wrong result.
Personally I don’t recall ever running into the need that you describe; that doesn’t mean it cannot happen but certainly it is not common. The case is delicate enough that we should not go for some possibly deceptive (“gotcha”) special semantic rule. Instead it is so easy if you really run into this need to write a procedure set_from_B (u: B) in A, or its function counterpart in B, defining the exact semantics that you want, respectful of course of the contracts. Then you can even declare the routine as a conversion procedure or conversion function, so that your desired assignment syntax
a := b
works. But not with some murky effect. With one that you have explicitly define to implement your desired behavior.
I think this is another example in which the design of Eiffel has considered issues that in other languages are completely overlooked, leaving individual programmers to grapple with the consequences. I know we should explain all of this more.
-- BM
From: eiffel...@googlegroups.com <eiffel...@googlegroups.com> On Behalf Of Alexey Kanatov
Sent: Tuesday, February 5, 2019 17:03
To: Eiffel Users <eiffel...@googlegroups.com>
Subject: [eiffel-users] 3 small questions
Hi all, just wonder
1. why () are prohibted in ISE Eiffel
make () do
foo ()
end
is no longer a valid syntax ?
2. What is wrong with a: expanded A - why is it treated as obsolete?
3. Expanded conformance
class A end
class B inherit A end
....
a: expanded A
b: expanded B
...
a:= b -- validity rule violation - why? What is wrong with polymorphism for expanded obejcts?
Tnx, Alexey
P.S. I did not use Eiffel for years and not aware of latest changes ... so, just wonder ...
--
You received this message because you are subscribed to the Google Groups "Eiffel Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to eiffel-users...@googlegroups.com.
Visit this group at https://groups.google.com/group/eiffel-users.
For more options, visit https://groups.google.com/d/optout.
…
local
a: expanded A
b: expanded B
i: INTEGER
do
….
a := b -- Let’s assume it is allowed
i := a.x -- Will it be postcondition violation or not? yes it will ! But if we remove 'expanded' from declarations of a and b will we get the same effect?
-- a.x will invoke A version of x as there is no dynamic disptach for expanded types - so, the trick is that creation procedure for B is absent which should set field xx into the state which corresponds to postcondition of x ...
-- With 'reference' semantics relies on the body of redefined x and as it retrun yy which is 0 teh same postcondition inherited from A version will trigger alarm ...
-- So, this example shows that skipping invaraints or better say replacing invariants with postconditions is very obscure and danguerous pattern of programming ...
…
end
class A
create
make
feature {NONE}
xx: INTEGER
make do
xx := 1
end
feature
x: like xx do
Result := xx
ensure
Result > 0
end
set_x (a: like xx)
require
a > 0
do
xx := a
end
end
class B
inherit
A redefine x, set_x end
feature
yy: Integer
x: like xx do
Result := yy
end
set_x (a: like xx)
do
yy := a
end
end
Dear Alexey,
If I understand you right you are saying that my example is bad style. In fact I think that the example, not itself but as an abstraction of things that people do, is completely reasonable. I am sure I could build a realistic example, and transpose the problem to invariants from postconditions.
But the point here is not about style. What I believe I showed through the example is that the “partial extraction” semantics of expanded-to-expanded polymorphic assignment, whose absence in Eiffel you deplore, leads to incorrect behavior, which does not occur with standard reference-to-reference polymorphism.
The task of a programming language design is to define consistent semantics for programs. The language design cannot be judgmental: it is not its business, or the compiler’s, to decide that certain patterns of programming are “very obscure and dangerous”. More precisely, if the language designers make such a determination and are sure of the obscurity and dangerosity, they change the language design to make them invalid, hence rejected at compile-time. For programs that the language definition and hence the compilers accept, they should guarantee well-defined processing according to general and clear rules. Between the designers and the users of the language this is a kind of, if I may use that term, contract.
That aspect has always shocked me in the design of languages other than Eiffel. Java, for example, followed C++ in mistaking the notion of exporting an attribute for the notion of exporting it read-write. Then the official language description warned readers about using this scheme, saying it’s bad and the attribute should be shadowed by a function. It seems schizophrenic, hypocritical or both to include a feature in a language that you have built yourself (no one forced you either to design the language or include the feature) and in the same breadth tell people not to use it. Eiffel stays away from any such situation. If we put something in the language, it’s for people to use it.
Of course there remains room for style advice, as illustrated by the code analyzer (ex-“Eiffel Inspector”) of EiffelStudio, which warns programmers of code elements of sub-par style. But that’s to help them improve their code, not as an excuse for deficiencies in the language.
Dear Alexey,
If I understand you right you are saying that my example is bad style. In fact I think that the example, not itself but as an abstraction of things that people do, is completely reasonable. I am sure I could build a realistic example, and transpose the problem to invariants from postconditions.
based on the example I've created I've got two things - first that redefined function x does nothing to preserve the inherited postcondition which leads to postcondition violation (IMHO regardsless of expanded or reference entity is used as target for the call). Second was my attempt to fix it thru proper creation procedure and making the postcondition on the value of class attribute to become class invariant. That is why I put the comment that the semantics of the function which does nothing to preserve inherited postcondition is dangerous.
But the point here is not about style. What I believe I showed through the example is that the “partial extraction” semantics of expanded-to-expanded polymorphic assignment, whose absence in Eiffel you deplore, leads to incorrect behavior, which does not occur with standard reference-to-reference polymorphism.
The task of a programming language design is to define consistent semantics for programs. The language design cannot be judgmental: it is not its business, or the compiler’s, to decide that certain patterns of programming are “very obscure and dangerous”. More precisely, if the language designers make such a determination and are sure of the obscurity and dangerosity, they change the language design to make them invalid, hence rejected at compile-time. For programs that the language definition and hence the compilers accept, they should guarantee well-defined processing according to general and clear rules. Between the designers and the users of the language this is a kind of, if I may use that term, contract.