June 02, 2013
On 06/02/2013 10:44 PM, Jonathan M Davis wrote:
> On Sunday, June 02, 2013 12:37:38 Andrei Alexandrescu wrote:
>> On 6/2/13 9:59 AM, Manu wrote:
>>> I've never said that virtuals are bad. The key function of a class is
>>> polymorphism.
>>> But the reality is that in non-tool or container/foundational classes
>>> (which are typically write-once, use-lots; you don't tend to write these
>>> daily), a typical class will have a couple of virtuals, and a whole
>>> bunch of properties.
>>
>> I've argued if no dispatch is needed just make those free functions.
>> _Everything_ in a class is supposed to be overridable, unless inherited
>> and explicitly "final"ized. It's sort of a historical accident that
>> things got the way they are. But in D we know better because we have the
>> module-level privacy model and UFCS. So we should break clean from history.
>
> There are four problems with that:
>
> 1. Very few programmers think that way. The normal thing in most every OO
> language is to put all of the functions on the class, so pretty much no one is
> going to make them free functions. Do you really expect people to put
> properties (what would have been getters and setters in other languages)
> outside the class? It's not going to happen. And by default, everyone takes a
> performance hit as a result (folks like Manu and Don wcare about that
> performance hit more than many of us, but it's still there).
> ...

Yup.

> 2. The class' functions are no longer encapsulated inside the class. For some
> programmers, this is a big deal. They want all of the class' functionality on
> the class where it's easy to find. Having UFCS makes using free functions less
> of a problem, but many programmers will absolutely hate the idea of putting
> their non-virtual functions outside of the class, so they won't do it, and
> they (and everyone using their code) will end up with virtual functions when
> the functions shouldn't be virtual.
>

This is 1. again. (The unit of encapsulation in D is a module.)

> 3. In many cases, putting a function outside of the class is a royal pain.
> This is particularly true with templated classes. If you have
>
> class C(T)
> {
>     private T _var;
> }
>
> and you want a property to give you var, you end up with something ugly like
>
> @property auto var(U)(U this_)
>      if(is(U V == C!W, W))
> {
>      return this_._var;
> }
>
> Do you really expect many programmers to be able to pull off complicated is
> expressions like that? _I_ have to look it up every time I use it. Sure, using
> free functions might work in simple cases, but it starts falling apart when
> you have to deal with stuff like templated types.
>

1. Just do

@property auto var(T)(C!T this_){ return this_._var; }

2. The is-expression is not that complicated. The latest release finally fixes the issue that you needed to specify a redundant identifier to make it work:

@property auto var(U)(U this_)if(is(U==C!T,T)){ return this_._var; }


> 4. It causes more ambiguities and compilation errors. If a function is on the
> class, it always wins. If it's a free function, then you potentially have
> ambiguities due to overload sets. Making sure that the function takes the
> exact type significantly reduces the problem, but someone else could easily
> create a function with the same signature which now conflicts with the one
> which is effectively supposed to be a member function.
> ...

Well, yes, but I think this will not be a huge problem in practise.
(The private symbol clash stupidity may exacerbate the problem when it occurs though.)

I wouldn't mind if the default changed. (But I currently cannot update my compiler anyway, because of hard-to-reduce forward reference bugs.)
June 02, 2013
On Sunday, June 02, 2013 13:44:33 Jonathan M Davis wrote:
> On Sunday, June 02, 2013 12:37:38 Andrei Alexandrescu wrote:
> > On 6/2/13 9:59 AM, Manu wrote:
> > > I've never said that virtuals are bad. The key function of a class is
> > > polymorphism.
> > > But the reality is that in non-tool or container/foundational classes
> > > (which are typically write-once, use-lots; you don't tend to write these
> > > daily), a typical class will have a couple of virtuals, and a whole
> > > bunch of properties.
> > 
> > I've argued if no dispatch is needed just make those free functions. _Everything_ in a class is supposed to be overridable, unless inherited and explicitly "final"ized. It's sort of a historical accident that things got the way they are. But in D we know better because we have the module-level privacy model and UFCS. So we should break clean from history.
> 
> There are four problems with that:
[snip]

Another issue is extern(C++). In discussions at dconf, it became clear that we need to support non-virtual member functions with extern(C++) in order to move the compiler to D. Daniel Murphy has been working on this and could give better details, but the UFCS trick isn't going to work for that, and the fact that functions are virtual by default makes it problematic at best and possibly prevents it outright. We actually need extern(C++) classes.

- Jonathan M Davis
June 02, 2013
Joseph Rushton Wakeling:

> I have to say that it'd be very tempting to try and organize an annual event
> (and maybe also an online space) where researchers using computation are brought
> together with genuinely expert developers for lectures, brainstorming and
> collaboration, with the specific aim of getting away from these kinds of habitual errors.

It seems a nice idea. But I think it's better for this event to be also a chance for developers to learn something from researchers, things like the value of having a control, why people do scientific research, etc :-)

Bye,
bearophile
June 02, 2013
On 06/02/2013 11:31 PM, bearophile wrote:
> It seems a nice idea. But I think it's better for this event to be also a chance for developers to learn something from researchers, things like the value of having a control, why people do scientific research, etc :-)

Sure. :-)
June 03, 2013
On 3 June 2013 01:53, Roy Obena <roy.u@gmail.com> wrote:

> On Sunday, 2 June 2013 at 14:34:43 UTC, Manu wrote:
>
>> On 2 June 2013 21:46, Joseph Rushton Wakeling
>>
>
>  Well this is another classic point actually. I've been asked by my friends
>> at Cambridge to give their code a once-over for them on many occasions,
>> and
>> while I may not understand exactly what their code does, I can often spot
>> boat-loads of simple functional errors. Like basic programming bugs;
>> out-by-ones, pointer logic fails, clear lack of understanding of floating
>> point, or logical structure that will clearly lead to incorrect/unexpected
>> edge cases.
>> And it blows my mind that they then run this code on their big sets of
>> data, write some big analysis/conclusions, and present this statistical
>> data in some journal somewhere, and are generally accepted as an authority
>> and taken seriously!
>>
>
> You're making this up. I'm sure they do a lot of data-driven tests or simulations that make most errors detectable. They may not be savvy programmers, and their programs may not be error-free, but boat-loads of errors? C'mon.
>

I'm really not.
I mean, this won't all appear in the same function, but I've seen all these
sorts of errors on more than one occasion.
I suspect that in most cases it will just increase their perceived standard
deviation, otherwise I'm sure they'd notice it's all wrong and look for
their bugs.
But it's sad if a study shows higher than true standard deviation because
of code errors, or worse, if it does influence the averages slightly, but
they feel the result is plausible within their expected tolerance.
The scariest state is the idea that their code is *almost correct*.

Clearly, they should be using D ;)


June 03, 2013
On 3 June 2013 02:37, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org>wrote:

> On 6/2/13 9:59 AM, Manu wrote:
>
>> I've never said that virtuals are bad. The key function of a class is
>> polymorphism.
>> But the reality is that in non-tool or container/foundational classes
>> (which are typically write-once, use-lots; you don't tend to write these
>> daily), a typical class will have a couple of virtuals, and a whole
>> bunch of properties.
>>
>
> I've argued if no dispatch is needed just make those free functions.


You're not going to win many friends, and probably not many potential D
users by insisting people completely change their coding patterns that
they've probably held for decades on a trivial matter like this.
And if that's to be realistic, there are some problems that need to be
addressed with that. The template issue for a start.

_Everything_ in a class is supposed to be overridable, unless inherited and
> explicitly "final"ized.


Who says? I certainly wouldn't say that. Sounds like a horrible idea to me. C++ and C# users would never say that.

It sounds like a disaster waiting to happen to me. There are functions that
the author intended to be overridden, and functions that have no business
being overridden, that the author probably never imagined anyone would
override.
What if someone does come along and override one of these, and it was never
designed to work under that circumstance in the first place?
At very least, it will have never been tested. That's not a very robust API
offering if you ask me.

It's sort of a historical accident that things got the way they are. But in
> D we know better because we have the module-level privacy model and UFCS. So we should break clean from history.


Why? Can you actually justify what's bad about a class containing it's properties/accessors? (I've read the article you pointed me at once, it was interesting, but didn't convince me)


Interestingly, you didn't actually disagree with my point about the common case here, and I don't buy the Java doctrine.


June 03, 2013
On Monday, 3 June 2013 at 07:06:05 UTC, Manu wrote:
> There are functions that
> the author intended to be overridden, and functions that have no business
> being overridden, that the author probably never imagined anyone would
> override.
> What if someone does come along and override one of these, and it was never
> designed to work under that circumstance in the first place?
> At very least, it will have never been tested. That's not a very robust API
> offering if you ask me.

This is something just as important as the performance issues. Most of the time people will leave functions to simply use whatever the default is for virtual/final. If it's final, this works fairly well. The author hasn't put in the effort to decide how to handle people overriding their function. But with virtual by default, you don't know if the author actually considered that people will be overriding the function or if it's simply that they didn't bother specifying. I know the vast majority of my code is virtual, simply because I didn't specify the 'final' keyword 500 times, and didn't think about that I'd need to do it. The resulting code is unsafe because I didn't consider those functions actually being overridden.

A standard example is the addRange vs add functions. If these are left as default, you have no clue if the author considered that someone would be overriding it. Will addRange call add, or do you need to override both? Is this an implementation detail that may change at any moment? Or when a different class overrides addRange and makes it call add? By forcing the author to write 'virtual', you know that they at least *considered* that someone may override it, and hopefully thought about the consequences or documented whether addRange calls add. Or they may think about it later and realize that it's a mistake to have left it default and make addRange final. Going from final to virtual is fine; going from virtual to final breaks code.

And of course, the vast majority of functions should *not* be virtual. So why is it the default to have to specify 'final' for all of these functions?

I think that there are substantial benefits to final being default, but breaking every single program that uses inheritance is a pretty big issue. Still, fixing the code would be quite trivial if you can see a list of the functions that need to be made virtual (either by specifying --transition=virtual or just looking at the compiler errors that pop up when you build). It would even be possible to write a tool to automatically update code using the results of --transition if it was implemented in a way that gave enough details. Unfortunately this would only solve the issue of making your code work again, you would still have to go through everything and decide if it should be virtual.
June 03, 2013
On Monday, 3 June 2013 at 07:30:56 UTC, Kapps wrote:
> On Monday, 3 June 2013 at 07:06:05 UTC, Manu wrote:
>> There are functions that
>> the author intended to be overridden, and functions that have no business
>> being overridden, that the author probably never imagined anyone would
>> override.
>> What if someone does come along and override one of these, and it was never
>> designed to work under that circumstance in the first place?
>> At very least, it will have never been tested. That's not a very robust API
>> offering if you ask me.
>
> This is something just as important as the performance issues. Most of the time people will leave functions to simply use whatever the default is for virtual/final. If it's final, this works fairly well. The author hasn't put in the effort to decide how to handle people overriding their function. But with virtual by default, you don't know if the author actually considered that people will be overriding the function or if it's simply that they didn't bother specifying. I know the vast majority of my code is virtual, simply because I didn't specify the 'final' keyword 500 times, and didn't think about that I'd need to do it. The resulting code is unsafe because I didn't consider those functions actually being overridden.
>

The whole concept of OOP revolve around the fact that a given class and users of the given class don't need to know about its subclasses (Liskov's substitution principle). It is subclass's responsibility to decide what it override or not, not the upper class to decide what is overriden by subclasses.

If you want to create a class with customizable parts, pass parameters to the constructor. This isn't OOP what OOP is about.

The performance concern is only here because things has been smashed together in a inconsequent way (as it is often done in D). In Java for instance, only overriden function are actually virtual. Everything else is finalized at link time. Which is great because you are able to override everything when testing to create mock for instance, while keeping good performance when actually running the application.
June 03, 2013
On 2013-06-03 10:11, deadalnix wrote:

> The whole concept of OOP revolve around the fact that a given class and
> users of the given class don't need to know about its subclasses
> (Liskov's substitution principle). It is subclass's responsibility to
> decide what it override or not, not the upper class to decide what is
> overriden by subclasses.
>
> If you want to create a class with customizable parts, pass parameters
> to the constructor. This isn't OOP what OOP is about.
>
> The performance concern is only here because things has been smashed
> together in a inconsequent way (as it is often done in D). In Java for
> instance, only overriden function are actually virtual. Everything else
> is finalized at link time. Which is great because you are able to
> override everything when testing to create mock for instance, while
> keeping good performance when actually running the application.

I've read a book, Effective Java, where it says, something like:

If you don't intend your class to be subclassed make it final, otherwise document how to subclass and which methods to override.

-- 
/Jacob Carlborg
June 03, 2013
On Monday, June 03, 2013 10:11:26 deadalnix wrote:
> The whole concept of OOP revolve around the fact that a given class and users of the given class don't need to know about its subclasses (Liskov's substitution principle). It is subclass's responsibility to decide what it override or not, not the upper class to decide what is overriden by subclasses.

It's the base class' job to define the API that derived classes will be overriding and the derived classes' choice as to exactly which functions they override (assuming that they're not abstract and therefore have to be overridden). That doesn't mean that the base class can't or shouldn't have other functions which are not intended to be overridden. Nothing about Liskov's substitution principle requires that the entire API of the base class be polymorphic.

- Jonathan M Davis