June 04, 2013
On Tuesday, 4 June 2013 at 05:41:16 UTC, Rob T wrote:
> Manu, I'm wondering that perhaps you should not be using classes at all. You can still create a similar overridable scheme for struct methods, and although it may not be as convenient, it will work. However a big failure point with stucts is the lack of inheritance.
>
> Structs would IMO be far more useful if they had inheritance. Inheritence can be fully removed from the rest of polymorphism, so there's no reason why structs which are not polymorphic cannot inherit.
>
> Actually I'd like to know why structs cannot inherit? I hate it when I end up creating classes when I have no other reason to create a class other than for the ability to inherit.
>

struct are value type. You can't know the size of a polymorphic type. So you'll have trouble sooner than you imagine.

The best part of that issue is that no problem occur when you copy only partially a struct, but then you corrupt memory when you call a virtual function that rely on thoses data. You can crash at this point, but more likely, you'll just corrupt memory, and the program will fail in some totally unrelated part of the code, depending on compiler switchs, in a non reproducible way.

I assure you this is the kind of problem you don't want to have.
June 04, 2013
On 6/4/13 1:41 AM, Rob T wrote:
> Structs would IMO be far more useful if they had inheritance.

We do offer subtyping via alias this.

Andrei
June 04, 2013
On 6/4/13 1:16 AM, Manu wrote:
> But unlike the first situation, this is a breaking change. If you are
> not the only user of your library, then this can't be done safely.

Same fallacy all over again, for the third time in this thread. You keep on going about "breaking change" without recognizing that the now broken code was happily taking advantage of the very flexibility that you argue was useless and needed fixing.

This is exactly the kind of argumentation that I disagree with, philosophically. Instead of carefully pondering the goods and the bads in a complex web of tradeoffs, it just clumsily gropes for the desired conclusion in ignorance of all that doesn't fit.

> If you write code like that, then write 'virtual:', it doesn't hurt
> anyone else. The converse is not true.

Fourth.

>     I look at making methods final specifically for optimization.  It
>     doesn't occur to me that the fact that it's overridable is a "leak"
>     in the API, it's at your own peril if you want to extend a class
>     that I didn't intend to be extendable.  Like changing/upgrading
>     engine parts in a car.
>
>
> Precisely, this highlights one of the key issues. Optimising has now
> become a dangerous breaking process.

Fifth.

[snip]

Allow me to summarize my understanding of the most valuable parts your argument.

* At the top level you believe ultimate efficiency should be default and OOP flexibility should be opt-in.

* Classes routinely should make most methods final because it's hard to imagine why one would override all but a few. Since those are a minority, it's so much the better to make final the default.

* Even the most performance-conscious people would not care to annotate classes and methods. It just doesn't happen. In contrast, people who want flexibility will annotate things for the simple reason they have to, otherwise overriding won't work.

* You don't consider it a problem that one must go back to base classes and changing methods from final to overridable, whenever such a need arises.

(It would be awesome if you had a similar list with the opposite arguments.)

If the above is an accurate summary, I'd say it's a matter in which reasonable people might disagree. I take issue with each of the points above (not flat out disagree with each, more like amend and qualify etc).

Unless fresh arguments, facts, or perspectives come about, I am personally not convinced, based on this thread so far, that we should operate a language change.


Andrei
June 04, 2013
On Tuesday, June 04, 2013 07:41:11 Rob T wrote:
> Manu, I'm wondering that perhaps you should not be using classes at all. You can still create a similar overridable scheme for struct methods, and although it may not be as convenient, it will work. However a big failure point with stucts is the lack of inheritance.
> 
> Structs would IMO be far more useful if they had inheritance. Inheritence can be fully removed from the rest of polymorphism, so there's no reason why structs which are not polymorphic cannot inherit.
> 
> Actually I'd like to know why structs cannot inherit? I hate it when I end up creating classes when I have no other reason to create a class other than for the ability to inherit.

How would it even work for a struct to inherit without polymorphism? The whole point of inheritance is to make it so that you can create types that can be used in place of another, and that won't work without references or pointers and polymorphism.

Use composition, and if you want to be able to call members of the inner struct on the outer struct as if they were members of the outer struct, then use alias this or opDispatch to forward them to the inner struct.

- Jonathan M Davis
June 04, 2013
On Tue, 04 Jun 2013 07:23:47 +0200, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 6/4/13 1:05 AM, Simen Kjaeraas wrote:
>> On Tue, 04 Jun 2013 06:16:45 +0200, Steven Schveighoffer
>> <schveiguy@yahoo.com> wrote:
>>
>>> I think it is unfair to say most classes are not base classes. This
>>> would mean most classes are marked as final. I don't think they are.
>>> One of the main reasons to use classes in the first place is for
>>> extendability.
>>
>> This is false. Consider this hierarchy: A->B->C, where x->y means 'x
>> derives from y'. There is only one base class (A), and only one class
>> that may be marked final (C). This will often be the case.
>
> You two are in violent agreement. (B is also a base class, in addition to being a derived class.)

Yes and no. I suspected after posting that this argument would appear.
I believe a degradation of jargon has taken place - Manu originally
spoke of foundation classes - classes with many overridable methods.
A in my example is one of these, and I believe that's what Manu meant
when he said 'base class' in the above discussion.


-- 
Simen
June 04, 2013
On Tuesday, 4 June 2013 at 05:58:32 UTC, Andrei Alexandrescu wrote:
> Unless fresh arguments, facts, or perspectives come about, I am personally not convinced, based on this thread so far, that we should operate a language change.
>

export => finalization as LTO ?
June 04, 2013
On 6/4/13 2:04 AM, deadalnix wrote:
> On Tuesday, 4 June 2013 at 05:58:32 UTC, Andrei Alexandrescu wrote:
>> Unless fresh arguments, facts, or perspectives come about, I am
>> personally not convinced, based on this thread so far, that we should
>> operate a language change.
>>
>
> export => finalization as LTO ?

I see some discussion about that at http://goo.gl/KIl8L, but am unclear on the exact idea. Is it some sort of class hierarchy analysis during link time?

Andrei
June 04, 2013
On Tuesday, June 04, 2013 02:10:40 Andrei Alexandrescu wrote:
> On 6/4/13 2:04 AM, deadalnix wrote:
> > On Tuesday, 4 June 2013 at 05:58:32 UTC, Andrei Alexandrescu wrote:
> >> Unless fresh arguments, facts, or perspectives come about, I am personally not convinced, based on this thread so far, that we should operate a language change.
> > 
> > export => finalization as LTO ?
> 
> I see some discussion about that at http://goo.gl/KIl8L, but am unclear on the exact idea. Is it some sort of class hierarchy analysis during link time?

The idea is that any function which isn't overridden shouldn't be virtual. However, in the normal case, there's no way of knowing whether a class will be derived from or whether any particular function will be overidden. Code can be compiled separately such that the compiler has no clue what derived classes exist.

However, in the case where you have a shared library and all symbols which will be used outside of the library must be exported, the linker could theoretically examine every function which is not exported and make it non- virtual if it's not being overridden, because no code outside of the shared library could possible override it. This will only help functions which aren't exported, and will only work on Windows, as no other platform currently requires that symbols be explicitly exported. It also requires that the linker do this, and as long as we're using the C linker, I don't know how it could. It would need to understand virtual functions (and possibly D virtual functions specifically) in order to make the optimization.

- Jonathan M Davis
June 04, 2013
On Tuesday, 4 June 2013 at 06:23:18 UTC, Jonathan M Davis wrote:
> The idea is that any function which isn't overridden shouldn't be virtual.
> However, in the normal case, there's no way of knowing whether a class will be
> derived from or whether any particular function will be overidden. Code can be
> compiled separately such that the compiler has no clue what derived classes
> exist.
>
> However, in the case where you have a shared library and all symbols which
> will be used outside of the library must be exported, the linker could
> theoretically examine every function which is not exported and make it non-
> virtual if it's not being overridden, because no code outside of the shared
> library could possible override it.

Yes.

> This will only help functions which aren't
> exported, and will only work on Windows, as no other platform currently
> requires that symbols be explicitly exported.

It can't work on windows right now, as export's semantic is not strong enough (it do not apply to virtual methods).

The proposal is to make export stringer (IE, makes it impossible to override class that aren't exported in a shared lib, on all systems.

Today's linker support the functionnality.

> It also requires that the linker
> do this, and as long as we're using the C linker, I don't know how it could.

linkers from GCC and LLVM toolchains allow for LTO. The concept is to dump the IR in the object file, so a compiler aware of it can run additional optimization at link time.

I don't know how hard it is for GCC, but for LLVM, what needs to be done is for the frontend to dump the right metadata into the IR, so an optimizing pass can remove unneeded virtual calls.

We can do it in a D specific way (using our own metadata and providing an optimization pas for LLVM) but most likely we won't even need to as the same feature is planned to be added to clang and we can most likely simply reuse clang's metadata.
June 04, 2013
On 6/4/2013 12:58 AM, Andrei Alexandrescu wrote:
>
> Unless fresh arguments, facts, or perspectives come about, I am
> personally not convinced, based on this thread so far, that we should
> operate a language change.
>

The best you could do without a language change is to establish some good conventions for class design:

A) prefer writing UFCS functions
B) make it the norm for classes to segment their functions a lot like the public/private setup of C++ classes:   Have blocks for final, override, final override.   Ideally there is a virtual keyword there like the others.


At least in C++ land I can tell if a code library is useful to me by searching for how it uses virtuals, among other things like searching for catches and throws since I am stuck on platforms without exception handling and am working in a codebase that is designed without it.


I also find the idea that the linker could help a bit iffy since once you start DLLEXPORT'ing your classes the dependency tree basically expands to your whole codebase and it quickly cascades into being unable to eliminate most functions and global variables from the final image. You end up with fat binaries and its hard to prune dead stuff, and it has to be done by hand by walking the map files for iffy-objects and tracking down their dependencies.  This is especially true if nearly everything is virtual.


I suppose in theory there is an alternative change which wouldn't require a new keyword, and that would be to make it so that methods are only virtual if they are defined in an interface, and forbidding them coming into existence at class scope.  But this would increase the code maintenance probably a bit too much.