Autore Topic: Ereditare, Wrapping e Conversioni di tipo  (Letto 1108 volte)

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.305
  • Ne mors quidem nos iunget
    • Mostra profilo
Ereditare, Wrapping e Conversioni di tipo
« il: 18 Aprile 2014, 12:27:55 »
Riporto questa discussione apparsa nella M.L. ufficiale:


" Suppose we are overriding the class 'Variant[]' to add the event
'Update' which will be raised after the completion of any method which
could possibly modify the array structure.  In particular, we need
to override appropriately any function that happens to do so.  For
exposition purposes let's consider the 'Extract' function, which removes
one or more elements from the array and returns them.  It's signature
is as follow:

Codice: gambas [Seleziona]
Public Function Extract(iStart As Integer, \
    Optional iLength As Integer) As Variant[]

Our inherited class has, hence, to override this function with another
of the exact same signature, by Gambas' rules.  However, it doesn't
make sense to return an object of the base class, while the operation
is executed by a derived class over an object thereof.

It seems there is no easy or natural way around it.  We could declare a
second extraction function for the derived class named after a different
symbol like "ExtractDerived", but we'd break the interface, what isn't
a desired outcome.

Another approach would be to forget this particular base class and go
upwards until there is no possible conflicting functions and to inherit
from there, what happens to be in this case the immediate base class of
'Variant[]' called "Array".  However, we'd have to re-implement large
portions of the 'Variant[]' class, that could otherwise be immediately
deployed if not by our particular design problem at hand.

One last, and arguably more reasonable, approach is to give up the
syntactical native language's feature of inheritance --- and its useful
properties -- and to define a dedicated container base class, which
inherits from nowhere, semantically wrapping all functionality of the
class 'Variant[]'.  It'd work as a layer for accessing the underlying
variant array which would be stored in a private variable and isolated
from the outside world, i.e., not directly accessible by any property.
The only way to modify the array's content would be to use the
class' interface which would mimic that of 'Variant[]'.

As we've seen, there is no way if not by modifications of the
interpreter's very core.  Thus, I'd like to know why Gambas enforces
the signature of functions onwards inherited classes.  If not by
technical reasons, but only for methodological consistency requirements,
I'd like to suggest that we make it, at least, allows to use the
inherited class in place of the parent class within method signatures.
Personally, I'd find yet more useful if there were no restriction at
all.

A very interesting and useful feature, related but independent of this
suggestion, is that the interpreter could take care of casting objects
in both directions in an hierarchy of classes.  In order to accomplish
it, there would be introduced two special methods which would be
implemented in any class the programmer deems them meaningful and
useful: '_castUp' and '_castDown', for casting an object from the
inherited to the parent class, and from the parent to the inherited
class, respectively.  Being 'ThisClass' the inherited class and
'UpClass' its parent, the special methods signatures would be:

Codice: gambas [Seleziona]
Static Public Function _castUp (hThisClass As ThisClass) As UpClass
Static Public Function _castDown (hUpClass As UpClass) As ThisClass

The job of the interpreter would be to call these functions, in a
possible chain, to cast automatically upside or downside every time an
object of certain class is used in a context that requires an object of
another class but in the same hierarchy.

What are your thoughts on that?

Thank you.
--
Bruno
"


" Well ... I'm surely not good as you are but I didn't understand the
meanning of castup or down... What is it for ?

Fabien
"


" Let me give you an abstract example.  Suppose we have the class
'SuperClass' which implements the function 'Frobnicate' that has the
following signature:

Codice: gambas [Seleziona]
Static Public Function Frobnicate(hSuperClass As SuperClass) \
    As SuperClass

Suppose now that we've extended our original class, and thus derived
the class 'SubClass', and let 'hSubClass' be any object thereof.
Suppose the function 'Frobnicate' was not overridden and that
'SuperClass.Frobnicate(hSubClass)' is generally not that useful because
the implicit identity casting doesn't generate a very meaningful
'SuperClass' object for the problem at hand.  However, we have a much
more interesting idea than the interpreter of how to map 'SubClass'
objects to meaningful and useful 'SuperClass' objects.  Here the
proposed special method '_castUp' comes in hand.  We add it to the code
of 'SubClass' in order to do the desired up-cast automatically:

Codice: gambas [Seleziona]
Static Public Function _castUp(hSubClass As SubClass) As SuperClass
    ' Do a magical conversion using arbitrary processing in accordance
    ' with our idea of a canonical map from 'SubClass' to 'SuperClass'.
  End

Now when the interpreter evaluates the expression
'SuperClass.Frobnicate(hSubClass)' it'll notice there is an up-cast
method and then it'll give this precedence over the implicit identity
casting.  Now we have a meaningful object to work with!

The '_castDown' special method works analogously but the other way
around.  The only major difference is that the interpreter doesn't have
a standard implicit identity cast in this direction and therefore raises
an error when one tries to use a 'SuperClass' object in a 'SubClass'
context.

The main point to emphasize here is that we are interested in
non-trivial castings.  Therefore, we need it both directions and
identity casts (the copy of common properties) doesn't suffice.

Bruno
"
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.305
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Ereditare, Wrapping e Conversioni di tipo
« Risposta #1 il: 19 Aprile 2014, 18:40:29 »
...continua...


" Hmm. I hacked the interpreter today - just to get a better idea of what is
going on in this case - and my patched gbx3 now accepts that you substitute,
e.g. VariantArray for Variant[] in the signature of VariantArray.Extract()
(of course, VariantArray Inherits Variant[]). Generally, if you override a
class' method or property, you can substitute any class farther up the
inheritance hierarchy with the overriding class. Here is a slightly related
post by Benoit about signature checks http://sourceforge.net/p/gambas/mailman/message/30783547/
 but I don't know if the reasoning applies to inherited classes, too... but continue reading:

After I got the signature accepted, there were type conversion problems when
I tried to convert the return value of Super.Extract(), which is Variant[],
as a VariantArray. I hacked that away too in the two possible ways:

(1) When VariantArray = Variant[], leave the value be a Variant[] behind the
    scenes. This imposed the problem that a subsequent call to Extract()
    would use Variant[]'s version and thus make the object useless (e.g. no
    Update events anymore).

(2) When VariantArray = Variant[], I told the type system that the Variant[]
    is legally a VariantArray and that made the interpreter segfault deep
    inside its gears (which was expected).

So there must be some serious technical reason against that. But only Benoit
can tell us the truth.

We could circumvent that crash when we simply disallowed that conversion and
had a method that would create a VariantArray from a Variant[], by copying
elements.

There is a _convert interface available for native classes (those written in
C/C++) to convert an object into another one of a different class. That this
is unavailable to Gambas programmers has obvious technical reasons. But the
one _convert() implementation I looked at (in gb.clipper), did the same: it
created a new object of the destination type and copied the relevant parts
of itself into that new object.

Oh, I forgot to ask: do you want the patch that allows inheritance-
compatible [1] classes to be used in place of a class in method and property
signatures to test the above approach - even if it's unlikely that it goes
mainline?

[1] Inheritance-compatibility is an antisymmetrical relation!


Regards,
Tobi
"


" Sure!  Reading http://sourceforge.net/p/gambas/mailman/message/30783547/
and http://code.google.com/p/gambas/issues/detail?id=78 , it seems that the main technical reason
Gambas doesn't support overriding signatures within inherited classes
is an optimization.  IMHO, the only way to justify such a restriction on
functionality is if the table symbol lookup were impracticably slow.
I'm usually much more concerned about powerfulness than speed.  I
think one language like Gambas should have its generality and
expressiveness maximized to the extent that its execution speed stays
reasonable.  I really like the "freedom" of choosing how to
structure my code.  The idea that the interpreter is passive about my
natural and intuitive programming creative process, as long it's still
logically consistent, is priceless.  Paraphrasing "The GNU Coding
Standards":
http://www.gnu.org/prep/standards/html_node/index.html

  The interpreter should be your servant, not your master.

bruno
"
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »

Offline vuott

  • Moderatore globale
  • Senatore Gambero
  • *****
  • Post: 11.305
  • Ne mors quidem nos iunget
    • Mostra profilo
Re: Ereditare, Wrapping e Conversioni di tipo
« Risposta #2 il: 21 Aprile 2014, 00:18:21 »
...continua...


" Attached. But be warned (again?): the interpreter is actually not my
business. Virtually, I don't even know what I've done to the code as an
entire with my patch but it worked in the single test case I had.

I tested code like this to have VariantArray implement Extract() and it
succeeded:

Codice: gambas [Seleziona]
Public Sub Extract(Start As Integer, Optional Length As Integer = 1) As VariantArray
  Dim hNew As New VariantArray
  Dim vElt As Variant

  For Each vElt in Super.Extract(Start, Length)
    hNew.Add(vElt)
  Next
  Raise ExtractEvent
  Return hNew
End

Note that VariantArray.Extract(), just like Variant[].Extract(), returns
a new object which has, by default, no event name. So don't be surprised
if the returned object doesn't fire any events. You have to manually assign
it to an event observer using Object.Attach(). Another reason against giving
events to data containers ;-)

>
> Thank you very much for your efforts coding on the interpreter.

Well, that's pretty much the only thing I can do when Benoit is not around
to answer those core questions (which is seldom the case anyway).

Regards,
Tobi



" I fixed your patch, as it didn't work when using the "Object" datatype.
You get it in revision #6249.

Regards,

--
Benoît Minisini
"
« Ultima modifica: 25 Aprile 2014, 00:51:57 da vuott »
« Chiunque, non ricorrendo lo stato di necessità, nel proprio progetto Gambas fa uso delle istruzioni Shell o Exec, è punito con la sanzione pecuniaria da euro 20,00 a euro 60,00. »