Jump to page: 1 24  
Page
Thread overview
Do non-member functions improve encapsulation in D?
Apr 20, 2014
monarch_dodra
Apr 20, 2014
Rikki Cattermole
Apr 20, 2014
Andrej Mitrovic
Apr 20, 2014
Gary Willoughby
Apr 20, 2014
monarch_dodra
Apr 20, 2014
Gary Willoughby
Apr 20, 2014
Gary Willoughby
Apr 21, 2014
Gary Willoughby
Apr 22, 2014
Jacob Carlborg
Apr 24, 2014
Dicebot
Apr 24, 2014
Gary Willoughby
Apr 24, 2014
Nick Treleaven
Apr 21, 2014
Andrej Mitrovic
Apr 21, 2014
Artur Skawina
Apr 21, 2014
Meta
April 20, 2014
In his article "How non-member functions improve encapsulation" [1], Scott Meyers makes a good argument for why free functions should generally be preferred over class methods in C++.  TL;DR: Fewer member functions means fewer functions that break when the class implementation changes, and free functions can be spread across different header files, allowing client code to only #include the ones that are needed.

In D, the situation is somewhat different.  Firstly, private symbols are accessible throughout the module within which the class/struct is defined, and secondly, UFC allows us to call free functions using the same syntax as member functions.  In other words, *any* non-virtual function could in principle be written as a free function.

The fact that "private" really means "module private" in D means that any number of functions can break when a class/struct implementation changes.  So if we are to take Meyers' advice, we have to define a new module for each class/struct, and move its associated free functions to neighbouring modules.  However, this would lead to a proliferation of modules that I doubt anyone would want.

So, can anyone think of some good guidelines for when to make a function a member, when to write it as a free function in the same module, and when to move it to a different module?


[1] http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197
April 20, 2014
On Sunday, 20 April 2014 at 07:11:41 UTC, Lars T. Kyllingstad wrote:
> In his article "How non-member functions improve encapsulation" [1], Scott Meyers makes a good argument for why free functions should generally be preferred over class methods in C++.  TL;DR: Fewer member functions means fewer functions that break when the class implementation changes, and free functions can be spread across different header files, allowing client code to only #include the ones that are needed.
>
> In D, the situation is somewhat different.  Firstly, private symbols are accessible throughout the module within which the class/struct is defined, and secondly, UFC allows us to call free functions using the same syntax as member functions.  In other words, *any* non-virtual function could in principle be written as a free function.
>
> The fact that "private" really means "module private" in D means that any number of functions can break when a class/struct implementation changes.  So if we are to take Meyers' advice, we have to define a new module for each class/struct, and move its associated free functions to neighbouring modules.  However, this would lead to a proliferation of modules that I doubt anyone would want.
>
> So, can anyone think of some good guidelines for when to make a function a member, when to write it as a free function in the same module, and when to move it to a different module?
>
>
> [1] http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

One thing to keep in mind, is that with the module system, and templates, is that free functions can only be called if the module *knows* about your free function.

For example "int[]" is a range thanks to the free "front/popFront", but also *because* `std.range` imports `std.array`, and as such *knows* about them.

If you tried the same thing yourself, with your user defined type, it wouldn't work.

From there, while I'm inclined to say that "yes, non-member functions improve encapsulation", everything that "defines" an object *must* be member. Anything non-member is second class, and "helper".
April 20, 2014
On Sunday, 20 April 2014 at 07:11:41 UTC, Lars T. Kyllingstad
wrote:
> In his article "How non-member functions improve encapsulation" [1], Scott Meyers makes a good argument for why free functions should generally be preferred over class methods in C++.  TL;DR: Fewer member functions means fewer functions that break when the class implementation changes, and free functions can be spread across different header files, allowing client code to only #include the ones that are needed.
>
> In D, the situation is somewhat different.  Firstly, private symbols are accessible throughout the module within which the class/struct is defined, and secondly, UFC allows us to call free functions using the same syntax as member functions.  In other words, *any* non-virtual function could in principle be written as a free function.
>
> The fact that "private" really means "module private" in D means that any number of functions can break when a class/struct implementation changes.  So if we are to take Meyers' advice, we have to define a new module for each class/struct, and move its associated free functions to neighbouring modules.  However, this would lead to a proliferation of modules that I doubt anyone would want.
>
> So, can anyone think of some good guidelines for when to make a function a member, when to write it as a free function in the same module, and when to move it to a different module?
>
>
> [1] http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

My rules that I try to adhere to are:
* Modules do one thing. All members of a module meet said modules
purpose.
I.e. std.traits is a great example of this, but std.algorithm not
quite so..
* Modules can have many free functions (I classify them as
checks) but few classes/structs unless its a defs file in which
case it can have quite a lot of them but few 'public' functions
and a lot of 'checks' functions.
* If it returns a new instance of a type and needs access to
private class/struct property then member.
* If it returns a new instance but doesn't need a types private
property then free.
* If it doesn't return a new instance of a type but may return
another type then member.

Generally speaking as long as you keep to the first (modules
purpose) rule then they stay smallish. Which is to me the most
important part.

Definition of a checks function is that they do one thing. They
get a single piece of information.
April 20, 2014
On Sunday, 20 April 2014 at 08:25:47 UTC, monarch_dodra wrote:
>
> One thing to keep in mind, is that with the module system, and templates, is that free functions can only be called if the module *knows* about your free function.
>
> For example "int[]" is a range thanks to the free "front/popFront", but also *because* `std.range` imports `std.array`, and as such *knows* about them.
>
> If you tried the same thing yourself, with your user defined type, it wouldn't work.

Very true.  This reminds me of another difference between D and C++, namely the fact that C++ supports ADL/Koenig lookup, while D doesn't.  If it did, arrays would no longer be special in this way.  I'm not sure whether the benefits of ADL outweigh the drawbacks, though.
April 20, 2014
On 4/20/14, Lars T. Kyllingstad via Digitalmars-d <digitalmars-d@puremagic.com> wrote:
> In his article "How non-member functions improve encapsulation" [1], Scott Meyers makes a good argument for why free functions should generally be preferred over class methods in C++.

TDPL actually references this IIRC.
April 20, 2014
On Sunday, 20 April 2014 at 07:11:41 UTC, Lars T. Kyllingstad wrote:
> In his article "How non-member functions improve encapsulation" [1], Scott Meyers makes a good argument for why free functions should generally be preferred over class methods in C++.  TL;DR: Fewer member functions means fewer functions that break when the class implementation changes, and free functions can be spread across different header files, allowing client code to only #include the ones that are needed.
>
> In D, the situation is somewhat different.  Firstly, private symbols are accessible throughout the module within which the class/struct is defined, and secondly, UFC allows us to call free functions using the same syntax as member functions.  In other words, *any* non-virtual function could in principle be written as a free function.
>
> The fact that "private" really means "module private" in D means that any number of functions can break when a class/struct implementation changes.  So if we are to take Meyers' advice, we have to define a new module for each class/struct, and move its associated free functions to neighbouring modules.  However, this would lead to a proliferation of modules that I doubt anyone would want.
>
> So, can anyone think of some good guidelines for when to make a function a member, when to write it as a free function in the same module, and when to move it to a different module?
>
>
> [1] http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

This is a quote from Walter that sums the reasoning up perfectly:

"A huge reason for them is to head off the temptation to write ‘kitchen sink’ classes that are filled with every conceivable method. The  desired approach is to have the class implement the bare minimum of functionality, and add other functionality with extension methods (that do not have access to the class’ private state)."

Writing classes like this allows for better encapsulation because only the required behaviour is contained within the class keeping it focused. Other methods that are useful for the class (but don't really belong in the class) can be implemented as non member function and if written in a generic way be reused throughout the program.
April 20, 2014
On Sunday, 20 April 2014 at 11:01:27 UTC, Gary Willoughby wrote:
>
> This is a quote from Walter that sums the reasoning up perfectly:
>
> "A huge reason for them is to head off the temptation to write

What does "them" refer to here?

> ‘kitchen sink’ classes that are filled with every conceivable method. The  desired approach is to have the class implement the bare minimum of functionality, and add other functionality with extension methods (that do not have access to the class’ private state)."

However, in D, all functions defined in the same module as a class will have access to the private state of that class, on an equal footing with its member methods.  Therefore, the above statment doesn't really help in deciding which to use.

> Writing classes like this allows for better encapsulation because only the required behaviour is contained within the class keeping it focused. [...]

I'm also pretty sure Walter has repeatedly stated that the module is the unit of encapsulation in D, not the class.
April 20, 2014
On Sunday, 20 April 2014 at 11:12:42 UTC, Lars T. Kyllingstad wrote:
> On Sunday, 20 April 2014 at 11:01:27 UTC, Gary Willoughby wrote:
>> ‘kitchen sink’ classes that are filled with every conceivable method. The  desired approach is to have the class implement the bare minimum of functionality, and add other functionality with extension methods (that do not have access to the class’ private state)."
>
> However, in D, all functions defined in the same module as a class will have access to the private state of that class, on an equal footing with its member methods.  Therefore, the above statment doesn't really help in deciding which to use.
>
>> Writing classes like this allows for better encapsulation because only the required behaviour is contained within the class keeping it focused. [...]
>
> I'm also pretty sure Walter has repeatedly stated that the module is the unit of encapsulation in D, not the class.

Wouldn't this be "worked around" by packages?

You place your class in a non-public subpackage package. Then you implement the non-member functions in the module proper.

MyModule
  |
  +--MyPackage
  |    |
  |    +-MyClass
  |
  +--MyNonMemberFunctions


Not sure this is "worth it" though.
April 20, 2014
On Sunday, 20 April 2014 at 11:12:42 UTC, Lars T. Kyllingstad wrote:
>> Writing classes like this allows for better encapsulation because only the required behaviour is contained within the class keeping it focused. [...]
>
> I'm also pretty sure Walter has repeatedly stated that the module is the unit of encapsulation in D, not the class.

That may be true but doesn't detract anything from the quote and doesn't mean that classes shouldn't be well designed with good encapsulation in mind.
April 20, 2014
On Sunday, 20 April 2014 at 11:12:42 UTC, Lars T. Kyllingstad wrote:
> However, in D, all functions defined in the same module as a class will have access to the private state of that class, on an equal footing with its member methods.  Therefore, the above statment doesn't really help in deciding which to use.

Yeah it does. If the function can be used generically across many different parts of the program then it would be much better implemented as a non-member function, even if it's defined in the same module as an associated class.

Functions which are focused to only deal with data associated with a particular class then these would be better suited to be implemented as a method of that class.

I'm sure there are edge cases but i'm pretty sure this is the general idea.

For example: I worked on a project which included a class that internally used a method to perform some math for internal data. This method was not related to the class in any way, it just performed some calculation on data within that class. This is the sort of method that would be better served pulling out of that class and moving into a library to be reused elsewhere if needed. Thus the class becomes more focused on what its actually supposed to do while becoming more maintainable.
« First   ‹ Prev
1 2 3 4