September 08, 2013
On 2013-09-07, 19:00, Walter Bright wrote:

> Outlining of member functions is the practice of placing the declaration of a member function in the struct/class/union, and placing the definition of it at global scope in the module or even in another module.
>
> http://wiki.dlang.org/DIP47

I like the idea, but I feel the DIP is missing some info:

- What happens to UDAs? Do I need to declare them both places?
- Why no nested classes/structs?
- How do outlined ctors/dtors look?
- How does it look in practice? Is this how it works:

// foo.d:
module foo;
class Foo {
   int bar();
}

//foo_impl.d:
module foo;
import foo;
int Foo.bar() {
    return 3;
}

- Do the module names have to be the same?
- Do I have to import the 'header' module in the implementation modules?
- If the module names don't have to be the same:
    Can I implement the functions in any package? Sub-package? Only
        the same package?


Also, I disagree with these points:

- Parameter names should match. I can accept nameless parameters in the
   declaration, but otherwise they should match. Anything else is an
   invitation for things to go out of sync.
- Default parameter values - either disallow them or enforce that they
   are the same in both places.
- Implementation in a module different from the declaration. .di files
   provide all the good stuff here, without sending you on a wild goose
   chase through all your files for that one function your resident
   junior programmer hid away inside the implementation of a completely
   different class.


Lastly, I want more examples - is this the same for structs and classes?
Interfaces? Can I spread the function definitions over several modules?

All in all, I would currently vote against.


As an aside, I do like the idea of having a nice list of member
functions that is statically compared to those actually implemented.
This could however be done in different ways. My favorite would be
something along these lines:

class Foo {
    interface {
         float bar(int n) const;
         Foo clone();
         static int qux(); // Compile-time error on this line: declared
                           //  function with no implementation.
    }
    float bar(int n) const {
        return 0.0;
    }
    Foo clone() {
        return this;
    }
    static int baz() { // Compile-time error on this line: undeclared
                       //  function with implementation.
        return 2;
    }
}

For the sake of fueling discussion, I have created a DIP with this suggestion:
http://wiki.dlang.org/DIP48

-- 
  Simen
September 08, 2013
On Saturday, 7 September 2013 at 17:00:08 UTC, Walter Bright wrote:
> Outlining of member functions is the practice of placing the declaration of a member function in the struct/class/union, and placing the definition of it at global scope in the module or even in another module.
>
> http://wiki.dlang.org/DIP47

I am against this proposal. The rationale does not convince me we would be getting enough value out of solving this problem.

1. Converting C++ to D

This is the strongest argument. Maybe the issue identified here is solved with partial classes (see C#).

2. Having an outline of the code

DDOC already provides this! And any such limitations should be fixed. This point has several supporting points.

a. An IDE is not always available to fold code

I never do this, I hate code folding.

b. People read their code just as much in github commits, merge/diff windows, emails/chat, etc.

And? I could see some value having a summary for signature changes/additions when reviewing commits. For merges this proposal just adds one more line of conflict to deal with. Brining up emails would suggest that you want someone to write down their class signature; instead of having them go to that much work I'd rather ask them to generate the docs and email them to me. Chat, well I think that is a lost cause (I haven't had any code sent to me over chat that I had been glad to have received over chat)


I believe requiring the programmer to keep function prototypes in sync is a mistake. I also don't think having this be optional is addressing the issue of those that are for it.

When C/C++ programmers talk of the problems header files, they talk of the problems cause by textual replacements and compiled header files. When everyone who doesn't program in C/C++ talk of the problems of header files, they talk about the troubles of writing prototypes twice.

By providing this feature as an optional statement, you'll be left with Manu and his team using it, an no one else touching it (Sorry Manu, you're the only one I've seen with a strong conviction for it, I know others will use it too).

I realize that we want to make it as painless as possible for Remedy to switch from C++ to D (along with the rest of the game developers). I'm also really glad about the changes which have come from their use/insistence. However I think this is mostly a superficial change which will result in a divide in the community much like @property.
September 08, 2013
On 9/8/13, Jesse Phillips <Jesse.K.Phillips+D@gmail.com> wrote:
> I realize that we want to make it as painless as possible for Remedy to switch from C++ to D (along with the rest of the game developers).

FWIW I don't think this has anything to do with Remedy (afaik Manu
doesn't work there anymore).
September 08, 2013
I'm opposed to this DIP. It's aimed solely at aiding readability,
but having two ways to do something usually detracts from
readability. I don't see the point.
September 09, 2013
On 9/8/13 5:33 AM, Andrej Mitrovic wrote:
> On 9/8/13, Michel Fortin <michel.fortin@michelf.ca> wrote:
>> So I'd like to suggest this: allow a .d file to "import" its corresponding
>> .di file.
>
> This is actually what Andrei proposed as well.

I have to say I was a lot more in favor of the proposal before this thread.

The problem as I see it has two facets:

1. Code duplication

2. Modularity and logistics

Regarding (1), we currently force duplication of the entire class layout. My understanding is that this is the way it's done:

// file acme.di

class A {
  int x;
  double y;
  void fun();
}

// file acme.d
// cannot import acme.di
class A {
  int x;
  double y;
  void fun() { ... }
}

The fact that acme.d cannot import acme.di is an unforced error of embarrassing proportions and consequence. That should be fixed yesterday no matter how we proceed otherwise.

The problem with acme.d not having access to acme.di is that any error in duplicating the layout of A (e.g. swapping x and y or adding some other members etc) will have undefined behavior, and there is no reasonable way for the compiler to check against that.

Assuming that bug is fixed, the problem of duplication remains - all state of the class must be duplicated.

(I also suspect constructors might need to be white-boxed (i.e. available in the .di) for raw/cooked typechecking, but I'm not sure.)

If we go with DIP47, the duplication of state goes away. However we have a distinct problem - modularity, which segues into (2).

Allowing out-of-module implementations of individual methods poses additional modularity problems. Consider:

// file acme.di

class A {
  int x;
  double y;
  void fun();
}
private int a;
private void gun();

// file acme.d
// assume we solve the import problem
import acme;
void A.fun() { gun(); a = 42; }

If A.fun() were defined inside acme.di, it would have access to gun() and a. Defining it outside asks the question - do we allow such access, or not?

Intuitively the body of a method should not be all too sensitive to where it's placed, so that argues in favor of visibility.

D's module system has always favored a file-granular approach, e.g. private stuff is module-private. This notion of spilling private access outside the file into methods defined in various other files works against that nice tenet.

So it looks there's no obvious and obviously good solution. Probably the first one is more sensible.


Andrei

September 09, 2013
On 08/09/13 22:46, Gary Willoughby wrote:
> On Saturday, 7 September 2013 at 17:00:08 UTC, Walter Bright wrote:
>> Outlining of member functions is the practice of placing the
>> declaration of a member function in the struct/class/union, and
>> placing the definition of it at global scope in the module or even in
>> another module.
>>
>> http://wiki.dlang.org/DIP47
>
> I'm absolutely against this DIP.
>
> This proposal is just going back to the hell of header files again. Why
> on earth would you emulate C/C++ when D was supposed to be designed
> taking into account lessons learned from them. This is unnecessary
> complexity added for the sake of a few programmers who can't get out of
> C++ mode. I think you need to have a good hard think about *why* header
> files were introduced into those early languages and then consider if
> that reason is still valid. Personally i don't think it is. Java and C#
> do just fine without this.
>
> Seriously, this goes against everything you learn as a programmer,
> nothing should ever be typed twice and then to say that the declaration
> and implementation could be different just boggles my mind?!?! Great
> more work!
>
> If implemented, i will never used this feature and i will never deal
> with code that uses it either. I choose D *purely* because it didn't
> have this header file nonsense. If i find in future i start seeing more
> and more of this style of D code i would just move on to use something
> else that doesn't have all this extra baggage and work associated with
> it. Just because Manu brings it up randomly you decide to create a DIP?
>
> In reality this is a documentation issue. Which has already been
> addressed by DDOC or *.di files. If data exists in one form, and it is
> needed in another, that's work a computer should do. Not a human! IDE's
> also give you numerous tools to get class overviews and such. If you are
> suggesting that you also need these class overviews in code to be viewed
> on github etc, just use comments. They are as arbitrary and simpler to
> implement.
>
> Honestly this DIP is going backwards, i was under the impression D was
> going forwards! I am so disappointed.

Well said.

Peter

September 09, 2013
On Sun, Sep 08, 2013 at 02:53:10PM +0200, Dicebot wrote:
> On Sunday, 8 September 2013 at 12:46:49 UTC, Gary Willoughby wrote:
[...]
> >Seriously, this goes against everything you learn as a programmer, nothing should ever be typed twice and then to say that the declaration and implementation could be different just boggles my mind?!?! Great more work!
> 
> It is no different from overriding `interface` methods in class. From the code structure point of view, declaration is interface. Implementation is implementation. Keeping those separate may sometimes/often be useful.
[...]

I agree that declaration is interface, and implementation is implementation, and that it's good to separate them.  What I *don't* agree with is that the interface should be *manually* maintained.  There is absolutely no reason, in this day and age, that something so trivial as extracting the interface *automatically* and *reliably* by the compiler, can't be done.

Therefore, the *real* solution to this problem is to fix the compiler's .di output to give a proper overview of the class *automatically*, and nicely pretty-printed.  Manu has already said that the whole motivation behind wanting this sort of interface/implementation separation was to be able to tell what a class does at a glance. Well guess what? If we clean up the current messy .di generation to produce something decent, then all you have to do is to run dmd -H, and you have your at-a-glance version of the class.  No unnecessary complication of the language, no maintenance nightmare, no code duplication, very little implementation effort, and 100% reliable because the .di file is generated straight from the implementation, and therefore by definition is correct.

*This* is the correct solution to Manu's issue, IMO. DIP47 is approaching it from a completely wrong angle. Please, let's not go back to the C++ way. We've abandoned that a long time ago, and for good reason. That bridge should've been burned already.


T

-- 
INTEL = Only half of "intelligence".
September 09, 2013
On Sunday, 8 September 2013 at 03:37:35 UTC, Walter Bright wrote:
> On the other hand, DRY, and I don't recall anyone ever complaining about this in C++ outlined members.

Let me be the first, then, to formally lodge my complaint.

As for the DIP itself, I had enough of this tedium in C++. I would strongly prefer that it not be introduced in D. Working with Java/C#/D/any language with a decent module system, I've realized what a pain this "feature" is.
September 09, 2013
Missed the action...

Well it's clear this is not a popular proposal.
And even to me personally, it's certainly not of critical importance. If
there was a single thing I'd like to see *DONE* in D, it would be
temporary/r-value->ref args, without question (really, really annoying to
work around).

For the record, I tend to agree with the arguments of many in the 'against'
camp *from a purist point of view*, but the problem remains, and the reason
I raised it; this has demonstrated to me and my colleagues on a number of
occasions that it is a consistent productivity hindrance.
I guess a significant reason my experience seems to differ from many(/most)
on this forum, is that they're not in the business of banging out quick
throw-away commercial code.
The practical reality in my experience in the games industry is this:
 - Some (perhaps small) percentage of code is tech/foundation code. It is
carefully authored, but still typically documented only of the class
outline in the header file isn't already self-explanatory (I'm a big fan of
self-documenting code). It is to be re-used.
 - Much (perhaps most) code is rudimentary logic and glue code, or 'game
code' as we call it. This tends to be code that is written by more
inexperienced programmers and written to tight schedules. It is almost
never documented (apart from the occasional inline comments), rarely
conforms to some spec (unless it's implementing from foundational
interface), and usually exists to serve only the life of this one very
specific project, ie, throw-away. It is also almost always logic-intensive
code, riddled with switch/if/else constructs that take an awful lot of
vertical space, which further magnifies the problem of inline function
definitions breaking up the readability of the class outline.

Now further, from an absolutely practical point of view, the time that
someone unfamiliar with this code has to deal with it in the first place is
very likely at 11pm the night before the publisher is demanding a build so
they can show it to a bunch of arseholes in fancy suits who will decide if
they're going to pay us or not.
In C++ there is a very useful tool for unfamiliar programmers to get a good
brief overview of the code before they go hacking at it, that is the class
outline.
We've lost that. And I have demonstrably missed it.

People make claims like "write better code, split it up better, document
your code better, use the IDE folding", blah blah, but it's simply not the
practical reality. The budget and schedule does not typically allow for
careful consideration, design, and documentation of such throw away code
which glues the project together.
The code is what it is, and whether it's written in C++, or D, or lua, it's
probably not going to change the nature of this huge volume of crappy code.
Code folding doesn't work in foreign editors, communication tools, diff
windows; code reviews become more time consuming to approve, and I don't
know many programmers that like/use it regardless, even if it may help in
some occasions.
I think it's bat-shit-crazy to rely on a tool to simply make code readable
and easy to understand for a start. I also personally feel code folding
alienates me from the code I'm writing. As an author, you quickly gain a
feel for the layout of your source file; the flow and shape of the code
across the file is kinda picturesque. For me personally, code folding ruins
that relationship with my code, and I don't like it. It makes it harder for
me to sweep through my file and quickly locate the points of interest that
I'm working through. So if I'm not using it in my own code, but I'm
required to use it to understand someone else's code... there's a bit of a
conflict of interest there.

If I'm one of very few voices in favour of class outlines, I'm going to
suggest letting this argument sleep until other more serious issues are
resolved which people have been waiting on for ages.
I certainly want r-values -> ref args to work much more.

Keep in mind, I raised this debate as a practical account of last weekend,
among a bunch of other issues which were certainly more significant than
this one, but this seemed to capture the most attention.
It's not the first time it's come up and it'll be back again I'm sure :)

I support this DIP, obviously, but I'd suggest perhaps a conservative restriction that definitions should only be allowed to appear within the same module as the declaration. This would seem to simplify things like mangling issues and access rights. In my own use case, I have no reason to spread definitions across files. I just want to see class outlines clearly summarised at the top of the file. This saves time, and time is money.


On 8 September 2013 03:00, Walter Bright <newshound2@digitalmars.com> wrote:

> Outlining of member functions is the practice of placing the declaration of a member function in the struct/class/union, and placing the definition of it at global scope in the module or even in another module.
>
> http://wiki.dlang.org/DIP47
>


September 09, 2013
On 08/09/13 16:47, Walter Bright wrote:
> On 9/7/2013 11:08 PM, Peter Williams wrote:
>> In summary, you've gotten rid of the need for this type of duplication
>> so why
>> would you introduce it?
>
> I believe that is covered in the "Rationale" section of the dip.

Couldn't see a rational answer to my question in the rationale.

Looked more like a "I want to do this and I think that we should change the language syntax to allow it" statement to me.  It doesn't really add functionality just makes what's there more complex.

Peter