subclassing

spir denis.spir at gmail.com
Tue Nov 2 13:35:39 PDT 2010


On Tue, 02 Nov 2010 12:33:16 -0400
Jesse Phillips <jessekphillips+D at gmail.com> wrote:

> spir Wrote:
> 
> 
> > * Why does D allow me redefining (data) slots in the subclass OddSquares, which exist in the superclass? (I did this first without noting, by copy & paste ;-)
> 
> This is either a bug or so that you don't have name clashes with all the super classes (could really reduce the available names).

I have name clashes, that's what I meant with "redefining". Test case:

class SC {int i;}
class C : SC {int i;}

compiles. If this (non-)behaviour of the compiler is not intended for a good reason, then it may be considered a bug. At least, we should get a warning, don't you think? Also, this is anyway bad design and goes against the fact that in D (and OO in general), class features are precisely identified by name... See also, bug below.

> > * There is a bug in function find -- see DEBUG lines in this func and in main. For any reason, the passed range loses its data (.element==0). If I change find's interface to take a range of type OddSquares, then all runs fine. I don't know what more to do to find the bug...
> 
> Nope, the bug is because you redefined element and so Range.element is not being assigned. Remove your duplicate declarations from OddSquares and it works.

Yes! That(s it, thank you (tested). I identified the dstrange behaviour of the compiler above, but did not relate it to the bug.

> I am also not sure why you have alias T type. T is already an alias for the type, and you never use it so maybe it is just having fun testing out alias.

It for possible use in other app. Gives me access for the actual type from an instance, under instance.type. Found the trick in the book "The D P.L.".

I take the opportunity to ask whether there is a way for an element to access its own type, say x.type. I found typeid, but its only a string. Also, is there any kind of Type type, with variables of it? (Similar to function variables.) Finally, can an element know its (variable) name? Or can we set it on itself, provided it's a struct or class instance with a dedicated field, for example by reading the scope? (This is an idiom in Python, but sure it's a dynamic language.)

> > PS: I like very much this interface for input ranges :-) Decided to make output data plain state (slots T element & bool continues); only step() is a method. I know this is not true OO for some conception of OO, but it is ok for me.
> 
> This doesn't go against OOP in the least. The specification for a Range doesn't prevent the design you have choosen. The benefit to using functions is to calculate values instead of storing the state.

Right.

> I understand you are trying to discover the benefits through your own design,

Exactly! I find this whole feature really attractive, but complex and abstract. (In such cases, try-&-do-it-yourself is a learning method that works well with me, esp to understand the how's and why's.)

> but D Ranges would look something like this.
> 
> class OddSquares(T) {
>     // internal state
>     private T number ;
>     private T max ;
>     // methods
>     
>     @property bool empty() {
>         if (this.number > this.max)
>             return true;
>         return false;
>     }
>     @property int front() {
>         return this.number * this.number ;
>     }
>     void popFront() {
>         this.number += 2 ;
>     }
>     this(T min, T max) {
>         // set initial internal state
>         if (min%2 == 1)
>             this.number = min ;
>         else
>             this.number = min+1 ;
>         this.max = max ;
>     }
> }

Great, thank you for the example. I don't understand the benefit of @property (which indeed does not seem to have the same meaning as in python.)

> > I also like in this design the fact that the constructor sets the range's initial state -- or is supposed to -- so that we can use the range at once, without calling step() once to initialise it.
> 
> Ah, but you do call step at the very end of the constructor. This prevents it from entering user code and is generally all that matters.

I just don't want to force client code to initialize, since anyway the range is useless before init. With init in constructor, they get the range ready to use.

T find (T) (Range!T range , bool function(T element) predicate) {
    T element ;
    range.step();				/+ *** avoid this *** +/
    for (; range.continues ; range.step()) {
        element = range.element ;
        if (predicate(element))
            return element ;
    }
    return 0 ;  // placeholder, should be throw Exception
}

> You will notice I acutally removed that call for the range above.
> 
> I guess you could say that the real complaint was due to the desire to time how long it took to load the data (build the ranges) and how long it took to do the filtering. That meant I was separating the initialization phase from the construction of the range itself. Any way you rarely see this creep into user code.

I do not really understand whether you mean it's better w/o init in constructor, and why.

> So the only other piece to inform you of (to help you learn D) is that find is found in std.algorithm and does exactly what you have demonstrated, with a slightly different call:
> 
> auto element = find!((int e){return (e>200 && e<250))(range);
> 
> or
> 
> auto element = find!("a>200 && a<250")(range);
> 
> And will return a range starting at the element found, however it will not stop when over 250. For that you use filter instead.
> 
> auto element = filter!("a>200 && a<250")(range);
 
I considered making find (and other funcs using ranges) return the range, as is described in the article. But as of now, the only advantage I could guess is chaining method calls.

[OT topic="style"]
I don't use such patterns often because I prefere to clearly expose steps, for ease of reading, esp with carefully chosen names. eg in Python:

def listText1(elements, sep='', lDelim='',rDelim=''):
    return "%s%s%s" %(lDelim , sep.join(str(e) for e in elements) , rDelim)

def listText2(elements, sep='', lDelim='',rDelim=''):
    elementTexts = (str(e) for e in elements)
    content = sep.join(elementTexts)
    return "%s%s%s" %(lDelim , content , rDelim)

I'm sure a non-pythonist can easily read listText2, even with the strange expression on 1st line, less sure about listText1 :-)
[/OT]



-- -- -- -- -- -- --
vit esse estrany ☣

spir.wikidot.com



More information about the Digitalmars-d-learn mailing list