## digitalmars.D - Required Reading: "How Non-Member Functions Improve Encapsulation"

• Walter Bright (5/5) Oct 25 2017 for core D devs.
• rikki cattermole (21/30) Oct 25 2017 UFCS kills off a good part of those arguments, but point still stands.
• who cares (7/12) Oct 25 2017 I tend to agree. When a member function can be written with only
• Jacob Carlborg (6/11) Oct 26 2017 In D, protection attributes applies to the module. So if the free
• Kagamin (23/26) Oct 26 2017 You mean non-member functions are preferred? I encountered this
• Jacob Carlborg (4/10) Oct 27 2017 Adding methods to a struct will not increase its size.
• Kagamin (4/12) Oct 27 2017 Instance methods require this be passed by reference, which
• Jacob Carlborg (4/7) Oct 27 2017 Ah, right.
• w0rp (11/11) Oct 29 2017 I've noticed the benefits of writing non member functions in
• Jonathan M Davis (7/13) Oct 29 2017 Yeah, making functions generic can be a big win. The bigger question is ...
• Steven Schveighoffer (24/33) Oct 26 2017 I'm pretty sure I read that before.
• Dukc (10/30) Oct 27 2017 Assuming you don't want to change the original struct, this can
• JN (6/11) Oct 26 2017 As a counterpoint. I guess UFCS makes it less of a problem, but
• Jonathan M Davis (38/53) Oct 26 2017 At a previous job, we had our own string class which wrapped std::string
• Walter Bright (15/25) Oct 26 2017 The point is that functions that do not need access to private fields sh...
• Jonathan M Davis (57/82) Oct 26 2017 I agree that it's a good rule of thumb to aim for member functions to be...
• Kagamin (5/11) Oct 27 2017 Because it's functionality of the class. If it's not available,
• codephantom (18/27) Oct 31 2017 I'd like to do this too:
• Dmitry Olshansky (7/12) Oct 26 2017 The irony is that D’s private is “public to anyone in this
• Steven Schveighoffer (7/10) Oct 30 2017 You're missing a key piece here, in that anotherMethod does not ensure
• Jonathan M Davis (21/30) Oct 30 2017 Yeah, UFCS helps this whole concept, but the way that private works in D
• H. S. Teoh (64/95) Oct 30 2017 Yeah, the whole "private is module-private, not aggregate-private"
• codephantom (10/13) Oct 30 2017 I don't like it.
• Steven Schveighoffer (4/22) Oct 30 2017 I once thought as you do (though not as the syntax you propose). I now
• codephantom (11/14) Oct 30 2017 Yeah. I do like UFCS ... I was convinved after seeing Ali talk
• Steven Schveighoffer (5/24) Oct 30 2017 Except... then you can't use generic code with UFCS.
• codephantom (11/15) Oct 30 2017 Mmm... it sounds complex ;-)
• Jonathan M Davis (13/35) Oct 30 2017 Honestly, I think that generic code is the _only_ technical advantage of
• Patrick Schluter (5/29) Oct 30 2017 And under the hood the difference is also minimal. A member
• Dave Jones (4/14) Oct 31 2017 You dont need to know how its implemented. There's no benefit to
• H. S. Teoh (36/48) Oct 31 2017 The point is, you *shouldn't have to care*. If I use a library L that
• codephantom (18/21) Oct 31 2017 Yes, this is my main concern I guess, as I use pretty plain
• H. S. Teoh (17/31) Nov 01 2017 I'm a vim user, and frankly, I don't find the need for "enhanced"
• Dukc (13/22) Oct 31 2017 Yes, the idea here is great. It will work with range functions
• H. S. Teoh (83/108) Oct 31 2017 Haha, I knew somebody would bring this up. I don't have a good solution
• jmh530 (7/15) Oct 31 2017 Have you ever heard of the difference in how private works in D
Walter Bright <newshound2 digitalmars.com> writes:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or following these
guidelines. I expect we can do much better.

Oct 25 2017
rikki cattermole <rikki cattermole.co.nz> writes:
On 25/10/2017 11:19 PM, Walter Bright wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or following
these guidelines. I expect we can do much better.

UFCS kills off a good part of those arguments, but point still stands.

D
struct Point {
private int[2] d;

this(int x, int y) {
d[0] = x;
d[1] = y;
}

property {
ref int x() { return d[0]; }
ref int y() { return d[1]; }
}
}

void main() {
Point p = Point(1, 3);
p.y = 2;
}


Hehe ;)

Oct 25 2017
who cares <wsffws 12345678.cz> writes:
On Wednesday, 25 October 2017 at 22:19:23 UTC, Walter Bright
wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or
following these guidelines. I expect we can do much better.

I tend to agree. When a member function can be written with only
the public declarations (aka the "public API") it can be set as a
free function. However during my youth i've written lots of huge
classes...now that i don't write much anymore it's too late to
apply the rule :/

Oct 25 2017
Jacob Carlborg <doob me.com> writes:
On 2017-10-26 08:56, who cares wrote:

I tend to agree. When a member function can be written with only the
public declarations (aka the "public API") it can be set as a free
function. However during my youth i've written lots of huge
classes...now that i don't write much anymore it's too late to apply the
rule :/

In D, protection attributes applies to the module. So if the free
functions are defined in the same module, it's easy to accidentally
access private data.

--
/Jacob Carlborg

Oct 26 2017
Kagamin <spam here.lot> writes:
On Wednesday, 25 October 2017 at 22:19:23 UTC, Walter Bright
wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

You mean non-member functions are preferred? I encountered this
more from performance point: especially in case of small
structures like Point it would be more beneficial to pass them by
value than by reference, which can be achieved by extension
methods, but then you need to import the respective module to
have those extension methods available. It would work more
palatable if extension methods from the structure's module were
available without requiring import. It may rely on static
declarations inside the struct like C# does it:

struct Size
{
int width,height;
}

{
return Size(s1.width+s2.width, s1.height+s2.height);
}

or just work without it. Such alias would allow to provide
extension methods from other modules, though this would mean
circular dependency between modules.

Oct 26 2017
Jacob Carlborg <doob me.com> writes:
On 2017-10-26 12:42, Kagamin wrote:

You mean non-member functions are preferred? I encountered this more
from performance point: especially in case of small structures like
Point it would be more beneficial to pass them by value than by
reference, which can be achieved by extension methods, but then you need
to import the respective module to have those extension methods
available.

Adding methods to a struct will not increase its size.

--
/Jacob Carlborg

Oct 27 2017
Kagamin <spam here.lot> writes:
On Friday, 27 October 2017 at 07:36:45 UTC, Jacob Carlborg wrote:
On 2017-10-26 12:42, Kagamin wrote:

You mean non-member functions are preferred? I encountered
this more from performance point: especially in case of small
structures like Point it would be more beneficial to pass them
by value than by reference, which can be achieved by extension
methods, but then you need to import the respective module to
have those extension methods available.

Adding methods to a struct will not increase its size.

Instance methods require this be passed by reference, which
requires storage fiddling on the caller side. It's likely to
disappear after inlining, but still.

Oct 27 2017
Jacob Carlborg <doob me.com> writes:
On 2017-10-27 11:06, Kagamin wrote:

Instance methods require this be passed by reference, which requires
storage fiddling on the caller side. It's likely to disappear after
inlining, but still.

Ah, right.

--
/Jacob Carlborg

Oct 27 2017
w0rp <devw0rp gmail.com> writes:
I've noticed the benefits of writing non member functions in
Python codebases. Say if you have a User model in a Django ORM,
and you have a Thing model, and some operation on User and Thing.
I've noticed that your code is almost always better if you write
a non member function on User and Thing, instead of a member of
User or Thing.

Often a function belongs to neither type. Instead the logic cuts
across those two types. The key disadvantage I notice is ending
up with very large and unreadable classes which poorly categorise
business logic, when you could have been categorising functions
in modules based on different business needs.

Oct 29 2017
Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Sunday, October 29, 2017 08:45:15 w0rp via Digitalmars-d wrote:
I've noticed the benefits of writing non member functions in
Python codebases. Say if you have a User model in a Django ORM,
and you have a Thing model, and some operation on User and Thing.
I've noticed that your code is almost always better if you write
a non member function on User and Thing, instead of a member of
User or Thing.

Yeah, making functions generic can be a big win. The bigger question is what
to do when it doesn't really make sense to make the function generic, and it
doesn't need access to the private members of the type that it would always
be used with. In that case, it doesn't need to be a member function, but it
could be.

- Jonathan M Davis

Oct 29 2017
Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/25/17 6:19 PM, Walter Bright wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or following
these guidelines. I expect we can do much better.

I'm pretty sure I read that before.

However, D's lookup rules fail miserably when it comes to templates:

mod1.d:

auto callFoo(T)(T t)
{
return t.foo;
}

mod2.d:

struct S
{
int x;
}

int foo(S s) { return s.x * 5; }

void main()
{
auto s = S(1);
assert(s.foo == 5);
assert(s.callFoo == 5); // can't compile
}

Would be nice to have a way around this. Not sure what it would look like.

Also in D, it's harder to make this help for encapsulation since the
non-member functions would have to be in another module.

-Steve

Oct 26 2017
Dukc <ajieskola gmail.com> writes:
On Thursday, 26 October 2017 at 12:19:33 UTC, Steven
Schveighoffer wrote:
D's lookup rules fail miserably when it comes to templates:

mod1.d:

auto callFoo(T)(T t)
{
return t.foo;
}

mod2.d:

struct S
{
int x;
}

int foo(S s) { return s.x * 5; }

void main()
{
auto s = S(1);
assert(s.foo == 5);
assert(s.callFoo == 5); // can't compile
}

Would be nice to have a way around this. Not sure what it would
look like.

Assuming you don't want to change the original struct, this can
be worked around by making a wrapper type using alias this. I
think that's logical because you have to be explicit about which
foreign functions you want the imported algorithm to see. There
would be a function hijacking problem otherwise.

What is the catch here, is that alias this won't solve cases
where the free function takes a reference to the wrapped type or
returns it.

Oct 27 2017
JN <666total wp.pl> writes:
On Wednesday, 25 October 2017 at 22:19:23 UTC, Walter Bright
wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or
following these guidelines. I expect we can do much better.

As a counterpoint. I guess UFCS makes it less of a problem, but
it's nice when using an IDE to just type foo. , press ctrl-space
and see a nice list of methods that can be used with the class.
When having free functions, you don't get to enjoy that :)

Oct 26 2017
Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, October 26, 2017 12:53:38 JN via Digitalmars-d wrote:
On Wednesday, 25 October 2017 at 22:19:23 UTC, Walter Bright

wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/1844
01197

Note that I'm as guilty as anyone for not understanding or
following these guidelines. I expect we can do much better.

As a counterpoint. I guess UFCS makes it less of a problem, but
it's nice when using an IDE to just type foo. , press ctrl-space
and see a nice list of methods that can be used with the class.
When having free functions, you don't get to enjoy that :)

At a previous job, we had our own string class which wrapped std::string
just because some of the developers wanted our extra string functions on the
type where they would be easy to find (even just for documentation
purposes). They were of the opinion that once you separate the functions
from the class, your project will ultimately end up with multiple header
files of functions doing similar things, because at least some of the time,
own own. And with sufficiently large projects, that does seem to happen all
too often. I still don't agree with the idea that putting stuff on the class
is better just because it makes stuff easier to find, but I can see why
someone would think that.

As has been pointed out elsewhere in this thread, the encapsulation benefits
don't exist in the same way in D unless you put the free functions in
separate modules, and then you have to import other stuff to use them,
whereas in C++, you can just put the free functions next to the type and
still get the encapsulation benefits. So, the benefit of splitting the
functions out within a module is fairly minimal in D. Rather, the main
reason I see for splitting functions out is in order to make them more
generic, and I think that the push to do that in D has a tendency to make a
lot of functions free functions that might have been member functions in
most other languages.

And in D, there's the issue that declaring a function to be used with UFCS
instead of as a member function can be a bit of a pain when the type is
templated. You have to repeat a bunch of stuff that's just part of the
templated struct or class. You end up with additional templates and template
constraints (much of which is duplicated) just to separate the functions
from the type.

That being said, the time that I'm most likely to turn a member function
into a free function when the function isn't going to be made generic is
when the type is templated simply because then I can put the unittest block
right next to the function without having it be part of the template (since
that's almost always the wrong thing to do if you don't do some boilerplate
with static ifs to make it so that they're only compiled into a single
instantiation that's intended just for testing).

Overall, I think that the idea that functions should be free functions where
reasonable is good advice, but it needs to be taken with a grain of salt.

- Jonathan M Davis

Oct 26 2017
Walter Bright <newshound2 digitalmars.com> writes:
On 10/26/2017 3:05 PM, Jonathan M Davis wrote:
As has been pointed out elsewhere in this thread, the encapsulation benefits
don't exist in the same way in D unless you put the free functions in
separate modules, and then you have to import other stuff to use them,
whereas in C++, you can just put the free functions next to the type and
still get the encapsulation benefits. So, the benefit of splitting the
functions out within a module is fairly minimal in D. Rather, the main
reason I see for splitting functions out is in order to make them more
generic, and I think that the push to do that in D has a tendency to make a
lot of functions free functions that might have been member functions in
most other languages.

The point is that functions that do not need access to private fields should
NOT
be part of the class. Most of the discussion here is based on the idea that
those functions should still be semantically part of the class, even if not
physically. That idea should be revisited. Why should they be semantically part
of the class?

You can also do things like:

--- s.d -------
struct S { int x; ref int X() { return x; } }

--- splus.d ----
public import s;
int increment(S s) { s.X() += 1; } // note no access to S.x

--- user.d ----
import splus;
void foo(ref S s) { s.increment(); }

Oct 26 2017
Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, October 26, 2017 16:29:24 Walter Bright via Digitalmars-d
wrote:
On 10/26/2017 3:05 PM, Jonathan M Davis wrote:
As has been pointed out elsewhere in this thread, the encapsulation
benefits don't exist in the same way in D unless you put the free
functions in separate modules, and then you have to import other stuff
to use them, whereas in C++, you can just put the free functions next
to the type and still get the encapsulation benefits. So, the benefit
of splitting the functions out within a module is fairly minimal in D.
Rather, the main reason I see for splitting functions out is in order
to make them more generic, and I think that the push to do that in D
has a tendency to make a lot of functions free functions that might
have been member functions in most other languages.

The point is that functions that do not need access to private fields
should NOT be part of the class. Most of the discussion here is based on
the idea that those functions should still be semantically part of the
class, even if not physically. That idea should be revisited. Why should
they be semantically part of the class?

You can also do things like:

--- s.d -------
struct S { int x; ref int X() { return x; } }

--- splus.d ----
public import s;
int increment(S s) { s.X() += 1; } // note no access to S.x

--- user.d ----
import splus;
void foo(ref S s) { s.increment(); }

I agree that it's a good rule of thumb to aim for member functions to be the
set of functions that require access to private members, but I've never felt
that it's useful to be pedantic about it (particularly in D, where you don't
even get encapsulation if they're in the same module). When I write a struct
or class, it's generally designed with a set of operations that conceptually
go with it, and I don't see whether they need to access to private members
as being all that relevant to that. Functions which aren't really
conceptually part of the class or struct certainly should be separate, but
for me at least, it's about the API and what it's conceptually trying to do,
and what the type is trying to be and represent - what its abstraction is.
And in some cases, separating a function from a type just because it doesn't
happen to use any private members then feels like an artificial separation.

On some level, I think that it comes down to a question of what operations
are part of the abstraction and what operations are just using the
abstraction, if it's part of the abstraction, IMHO, it just makes more sense
for it to be a member function regardless of whether it accesses private
members.

Obviously, that's subjective, and there's then disagreement at least some of
the time on what should and shouldn't be a member function, but I don't
think that it's necessarily the case that having everything that doesn't
that a struct or class presents being very clean.

And once you get into classes and inheritance, breaking things up based on
what needs access to private members definitely falls apart in a number of
circumstances, because then you're very clearly abstracting behaviors (which
can then be overidden) as opposed to simply encapsulating data and operating
on it like some structs do. I just tend to think of types in general that
way, not just those involving interfaces and inheritance.

If you're coming at the type from the standpoint that everything that is
conceptually part of the type is actually part of the type, and everything
else is separate, then on some level, that will follow the idea that
functions that access private members are member functions and those that
don't aren't, but at least some of the time, it won't. And in that case, I'm
generally going to make it a member function.

I think that the problem is when folks just add functions to a type when
they don't need to be member functions to do what they do and really aren't
conceptually part of the type. They're just put on there because it's easy
or because that's what folks are used to having to do in languages like Java
or because it works better with auto-completion (which honestly, I think is
one area where IDEs make things worse; having auto-completion is fine, but
writing code in a certain way because of auto-completion is an anti-pattern
IMHO). Every time that a programmer is adding a function, they really should
be considering whether it really should be a member function or whether it
makes more sense for it to be a free function.

So, I think that the advice that member functions should generally be the
and something that programmers should keep in mind, but I also don't think
that that's really the best dividing line in general as to what should and
shouldn't be a member function. And thanks to how access levels work in D,
it's even less useful as a goal than it would be in C++, because structs and
classes simply aren't encapsulated in the same way, even if programmers
don't normally write code which breaks the encapsulation of structs and
classes unless they need to for the same reasons that you'd write a friend
function in C++.

- Jonathan M Davis

Oct 26 2017
Kagamin <spam here.lot> writes:
On Thursday, 26 October 2017 at 23:29:24 UTC, Walter Bright wrote:
The point is that functions that do not need access to private
fields should NOT be part of the class. Most of the discussion
here is based on the idea that those functions should still be
semantically part of the class, even if not physically. That
idea should be revisited. Why should they be semantically part
of the class?

Because it's functionality of the class. If it's not available,
it will be reimplemented and duplicated. C++ doesn't have such
problem, because in a way all imports are public there so you
have no chance to separate a function from type.

Oct 27 2017
codephantom <me noyb.com> writes:
On Thursday, 26 October 2017 at 23:29:24 UTC, Walter Bright wrote:
You can also do things like:

--- s.d -------
struct S { int x; ref int X() { return x; } }

--- splus.d ----
public import s;
int increment(S s) { s.X() += 1; } // note no access to S.x

--- user.d ----
import splus;
void foo(ref S s) { s.increment(); }

I'd like to do this too:
-------------------------
import std.stdio;

void main()
{
//int foo;
if (int foo.bar != 0)  // no, sorry, you cannot declare foo
here.
{
throw new Exception("foo.bar != 0");
}
}

auto bar(int x,)
{
return -10;
}

-----------------------------------------

Oct 31 2017
Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Wednesday, 25 October 2017 at 22:19:23 UTC, Walter Bright
wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or
following these guidelines. I expect we can do much better.

The irony is that D’s private is “public to anyone in this
module”. With that in mind free function or not you don’t get
anything.

Splitting it off to another module is clunky and I’d use it only
for functions that may work for different types (template).

Oct 26 2017
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Oct 25, 2017 at 03:19:23PM -0700, Walter Bright via Digitalmars-d wrote:
for core D devs.

"How Non-Member Functions Improve Encapsulation" by Scott Meyers

http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197

Note that I'm as guilty as anyone for not understanding or following
these guidelines. I expect we can do much better.

totally rawkz.  In D, we can take Scott's advice *without* suffering
from syntactic inconsistency between member and non-member functions:

// C++:
class C {
public:
int method();
private: ...
};

int anotherMethod(C &c, ...);

C c;
c.method();
anotherMethod(c);	// <-- syntactic inconsistency

// D:
class C {
public int method();
private: ...
}
int anotherMethod(C c, ...);

C c;
c.method();
c.anotherMethod();	// <-- Uniform syntax FTW!

Arguably, this means in D encapsulation is even better than in C++: the
user doesn't even have to care whether a function is a member or not.
The same syntax does the Right Thing(tm).

Furthermore, if the class implementation changes in a drastic way that
makes it possible to make a current member function a non-member, we can
do it in D without needing to touch any client code at all!

T

--
Кто везде - тот нигде.

Oct 30 2017
Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/30/17 1:40 PM, H. S. Teoh wrote:

totally rawkz.  In D, we can take Scott's advice *without* suffering
from syntactic inconsistency between member and non-member functions:

You're missing a key piece here, in that anotherMethod does not ensure
the encapsulation of C. It can call 'method' just fine.

Yes, it's great that UFCS can help with encapsulation via external
methods, but it's going to be difficult to prevent access to private
data, you have to use 2 modules. Not a very clean solution IMO.

-Steve

Oct 30 2017
Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, October 30, 2017 14:18:56 Steven Schveighoffer via Digitalmars-d
wrote:
On 10/30/17 1:40 PM, H. S. Teoh wrote:
totally rawkz.  In D, we can take Scott's advice *without* suffering

from syntactic inconsistency between member and non-member functions:

You're missing a key piece here, in that anotherMethod does not ensure
the encapsulation of C. It can call 'method' just fine.

Yes, it's great that UFCS can help with encapsulation via external
methods, but it's going to be difficult to prevent access to private
data, you have to use 2 modules. Not a very clean solution IMO.

Yeah, UFCS helps this whole concept, but the way that private works in D
means that compiler-enforced encapsulation simply doesn't happen at the type
level.

Another thing to think about is that private is private to the module rather
than the class or struct partly on the theory that you should be able to
keep track of everything in the module and maintain it appropriately so that
things like whether the type is fully encapsulated aren't really an issue. I
expect that that logic is mainly the justification for not implementing
friend as opposed to the actual reason, but it does pretty much fly in the
face of the idea that you need to be so worried about encapsulation that you
use free functions to prevent a function from accidentally using a private
member.

Still, UFCS does mean that it's less jarring to make something a free a
function, and it then fits well with the cultural push we have to make
functions generic, in which case, they're generally going to be free
functions. But the gains from making something generic are clear, whereas
the encapsulation gains of using a free function with UFCS are far less
substantial, if they exist at all.

- Jonathan M Davis

Oct 30 2017
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Mon, Oct 30, 2017 at 02:05:51PM -0600, Jonathan M Davis via Digitalmars-d
wrote:
On Monday, October 30, 2017 14:18:56 Steven Schveighoffer via Digitalmars-d
wrote:
On 10/30/17 1:40 PM, H. S. Teoh wrote:
totally rawkz.  In D, we can take Scott's advice *without*
suffering

from syntactic inconsistency between member and non-member
functions:

You're missing a key piece here, in that anotherMethod does not
ensure the encapsulation of C. It can call 'method' just fine.

Yes, it's great that UFCS can help with encapsulation via external
methods, but it's going to be difficult to prevent access to private
data, you have to use 2 modules. Not a very clean solution IMO.

Yeah, UFCS helps this whole concept, but the way that private works in
D means that compiler-enforced encapsulation simply doesn't happen at
the type level.

Another thing to think about is that private is private to the module
rather than the class or struct partly on the theory that you should
be able to keep track of everything in the module and maintain it
appropriately so that things like whether the type is fully
encapsulated aren't really an issue.

Yeah, the whole "private is module-private, not aggregate-private"
throws a monkey wrench into the works.  I can understand the logic
behind module-private vs. aggregate-private, but sometimes you really
*do* want aggregate-private, but D doesn't let you express that except
via splitting things up into submodules, which is a lot of overhead for
minor payback.

So my examples would have to involve submodules in order to really prove
the point.

[...]
Still, UFCS does mean that it's less jarring to make something a free
a function, and it then fits well with the cultural push we have to
make functions generic, in which case, they're generally going to be
free functions. But the gains from making something generic are clear,
whereas the encapsulation gains of using a free function with UFCS are
far less substantial, if they exist at all.

[...]

There's definitely gain here, IMO.  Part of the idea of encapsulation is
the independence of client code from implementation details, and one
such implementation detail is whether or not a function is implemented
as a class member or a free function. Ideally user code shouldn't need
to care about the difference.  In C++, however, user code has no choice,
because you can't write obj.func() when func is not a member.

But in D, UFCS allows obj.func() to work for both member functions and
free functions, so if the client code uses the obj.func() syntax, it
won't have to care about the difference.

One may argue about whether allowing arbitrary extensions to a class /
aggregate via UFCS is necessarily a good thing; but IMO, there are
definite benefits to it.  One classic example is std.range using UFCS to
essentially extend built-in arrays to have a range API, by providing
free functions .empty, .front, .popFront that take array arguments.  Of
course, whether this is a good thing can be argued for or against, but I
see this as just one example of a wider pattern of *adaptability*, which
IMO is a very good thing.

For example, suppose you're using a proprietary library that provides a
class X that behaves pretty closely to a range, but doesn't quite have a
range API.  (Or any other API, really.)  Well, that's not a problem, you
just write free functions that forward to class X's methods to bridge
the API gap, and off you go.  You don't have to work with your upstream
provider, who may not be able to provide a fix until months later, and
you don't have to create all sorts of wrapper types just to adapt one
API to another.

And if done right, if a new library version is released which breaks an
old API, say a method xyz() is deprecated and removed, or renamed, or
whatever, all you have to do is to write a free function named xyz, that
forwards to the new method(s), and you don't have to do a massive

(And yes, ideally the upstream library wouldn't break backward
compatibility, but we all know that in the real world it does happen
every now and then. And when it does, the last thing I want to be
worrying about is renaming 1500+ function calls to use xyz(p,q,r) syntax
instead of p.xyz(q,r) syntax. In C++, I'd have no choice, but in D, UFCS
lets my code become agnostic to this distinction, which is a very good
thing IMO.)

And as Scott already mentions, you can spice up the API you were given
with more convenience functions that do frequently-performed method call
sequences.  Carried one step further, this means if you have two
classes, presumably coming from two different vendors, with similar but
not-quite-the-same APIs, UFCS allows me to extend the two APIs so that
they converge.  Then my code can freely use objects from either library
without needing to care about API differences between them. Now,
*that's* encapsulation!

Basically, the more my code can become independent of API changes, the
better.  According to Scott's definition, that's an increase in
encapsulation, because the number of changes required to update my
codebase in the face of an API change is greatly reduced.  UFCS gives me
some pretty powerful tools in this regard.

T

--
Тише едешь, дальше будешь.

Oct 30 2017
codephantom <me noyb.com> writes:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
But in D, UFCS allows obj.func() to work for both member
functions and free functions, so if the client code uses the
obj.func() syntax, it won't have to care about the difference.

I don't like it.

When I see obj.func(), to me, func() is a member function. Why
should I spend any time trying to work out whether it's a member
function or a free function? It doesn't make sense to me.

If it's really a free function, I'd like that to be more
explicit..

e..g

obj.\func().   (or something like that ..where \ means its a free
function)

Oct 30 2017
Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/30/17 9:44 PM, codephantom wrote:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
But in D, UFCS allows obj.func() to work for both member functions and
free functions, so if the client code uses the obj.func() syntax, it
won't have to care about the difference.

I don't like it.

When I see obj.func(), to me, func() is a member function. Why should I
spend any time trying to work out whether it's a member function or a
free function? It doesn't make sense to me.

If it's really a free function, I'd like that to be more explicit..

e..g

obj.\func().   (or something like that ..where \ means its a free function)

I once thought as you do (though not as the syntax you propose). I now
embrace UFCS fully, it's awesome.

-Steve

Oct 30 2017
codephantom <me noyb.com> writes:
On Tuesday, 31 October 2017 at 01:47:39 UTC, Steven Schveighoffer
wrote:
I once thought as you do (though not as the syntax you
propose). I now embrace UFCS fully, it's awesome.

-Steve

Yeah. I do like UFCS ... I was convinved after seeing Ali talk

What a great ambassador for D he is!

But being able to differentiate whether a line of code is calling
a method of an object, or a free function... that's the area that
concerns me.

I'm lazy.. I would like such code to be more explicit....and not
leave it to me to work out what's going on.

Oct 30 2017
Steven Schveighoffer <schveiguy yahoo.com> writes:
On 10/30/17 9:59 PM, codephantom wrote:
On Tuesday, 31 October 2017 at 01:47:39 UTC, Steven Schveighoffer wrote:
I once thought as you do (though not as the syntax you propose). I now
embrace UFCS fully, it's awesome.

-Steve

Yeah. I do like UFCS ... I was convinved after seeing Ali talk about it:

What a great ambassador for D he is!

But being able to differentiate whether a line of code is calling a
method of an object, or a free function... that's the area that concerns
me.

I'm lazy.. I would like such code to be more explicit....and not leave
it to me to work out what's going on.

Except... then you can't use generic code with UFCS.

For example, arrays couldn't be ranges, because you can't just do
arr.front, you'd have to do arr.\front.

-Steve

Oct 30 2017
codephantom <me noyb.com> writes:
On Tuesday, 31 October 2017 at 02:10:26 UTC, Steven Schveighoffer
wrote:
Except... then you can't use generic code with UFCS.

For example, arrays couldn't be ranges, because you can't just
do arr.front, you'd have to do arr.\front.

-Steve

Mmm... it sounds complex ;-)

to make it worse, I can apparently call func() with the
parenthesis:

obj.func;

now, is func an attribute of obj?
is it a method of obj?
is it a free function?

these syntactic abstractions are leaky...we need to encapsulate
intent too ;-)

Oct 30 2017
Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, October 30, 2017 22:10:26 Steven Schveighoffer via Digitalmars-d
wrote:
On 10/30/17 9:59 PM, codephantom wrote:
On Tuesday, 31 October 2017 at 01:47:39 UTC, Steven Schveighoffer wrote:
I once thought as you do (though not as the syntax you propose). I now
embrace UFCS fully, it's awesome.

-Steve

Yeah. I do like UFCS ... I was convinved after seeing Ali talk about it:

What a great ambassador for D he is!

But being able to differentiate whether a line of code is calling a
method of an object, or a free function... that's the area that concerns
me.

I'm lazy.. I would like such code to be more explicit....and not leave
it to me to work out what's going on.

Except... then you can't use generic code with UFCS.

For example, arrays couldn't be ranges, because you can't just do
arr.front, you'd have to do arr.\front.

Honestly, I think that generic code is the _only_ technical advantage of
UFCS - everything else is just subjective preference about syntax. But the
fact that you can call a function without worrying about whether it's a
member function or a free function is huge for generic code - especially if
you want a type to be able to provide an optimized version of a function
(e.g. implementing its own find if it can implement find more efficiently
than the default linear implementation). Without UFCS, it gets way more
complicated to be able to call a function that might be a member function or
a free function, and in likelihood, the result would be that code would
always use one or the other rather than supporting both.

- Jonathan M Davis

Oct 30 2017
Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Tuesday, 31 October 2017 at 01:47:39 UTC, Steven Schveighoffer
wrote:
On 10/30/17 9:44 PM, codephantom wrote:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
But in D, UFCS allows obj.func() to work for both member
functions and free functions, so if the client code uses the
obj.func() syntax, it won't have to care about the difference.

I don't like it.

When I see obj.func(), to me, func() is a member function. Why
should I spend any time trying to work out whether it's a
member function or a free function? It doesn't make sense to
me.

If it's really a free function, I'd like that to be more
explicit..

e..g

obj.\func().   (or something like that ..where \ means its a
free function)

I once thought as you do (though not as the syntax you
propose). I now embrace UFCS fully, it's awesome.

And under the hood the difference is also minimal. A member
function is compiled to a free function in the object file. It's
only the mangled name that makes the difference.

Oct 30 2017
Dave Jones <dave jones.com> writes:
On Tuesday, 31 October 2017 at 01:44:58 UTC, codephantom wrote:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
But in D, UFCS allows obj.func() to work for both member
functions and free functions, so if the client code uses the
obj.func() syntax, it won't have to care about the difference.

I don't like it.

When I see obj.func(), to me, func() is a member function. Why
should I spend any time trying to work out whether it's a
member function or a free function? It doesn't make sense to me.

You dont need to know how its implemented. There's no benefit to
knowing. It's a waste of time / distraction to even think about
it.

Oct 31 2017
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Oct 31, 2017 at 01:44:58AM +0000, codephantom via Digitalmars-d wrote:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:

But in D, UFCS allows obj.func() to work for both member functions
and free functions, so if the client code uses the obj.func()
syntax, it won't have to care about the difference.

I don't like it.

When I see obj.func(), to me, func() is a member function. Why should
I spend any time trying to work out whether it's a member function or
a free function? It doesn't make sense to me.

The point is, you *shouldn't have to care*.  If I use a library L that
provides some type T, and the library provides operation X that I can
perform on type T, all I care about is that variables of type T can have
X performed on it.  Is X a member function? A free function?  Who cares!
That's just an implementation detail. As the user of library L, how T
and X are implemented are none of my business.  X can be a remote
procedure call to a network service for all I care -- my code doesn't
have to know.  All it needs to know is that type T can have operation X
performed on it.

That's the whole point of encapsulation.  You *don't have to know* how
something is implemented.  In fact, it's better that you don't, because
later when you upgrade library L, and X changes from member function to
free function, or vice versa, *your code doesn't have to change at all*.
That's why we care about encapsulation in the first place.  Why should
*I* have to change my code just because the upstream authors of library
L decides that X is better implemented as a free function vs. a member
function, or vice versa?  I don't care, and I shouldn't have to care.
Having the same interface (i.e., UFCS syntax) to call X regardless of
how it's implemented is a big advantage, because it frees me, the
downstream user, from needing to care about fiddly details of library
spend 10 hours fixing all my function calls to X, I can just upgrade
library L and let the compiler figure out which calls are member
functions and which are free functions. My code doesn't have to change
one bit.  Let the machine do the grunt work, free up the human to do the
higher level stuff.

The one case where the difference matters is when you're trying to debug
something.  In that case, I'd say the onus is really upon the debugger
to tell you what kind of function it was.  Surely the debugger must have
this information; it's just a matter of conveying the information to the
user adequately.  If current tools don't allow you to do this easily,
that's a problem with the tools, not with the concept of encapsulation.

T

--
Государство делает вид, что платит нам
зарплату, а мы делаем вид, что работаем.

Oct 31 2017
codephantom <me noyb.com> writes:
On Tuesday, 31 October 2017 at 15:45:42 UTC, H. S. Teoh wrote:
The one case where the difference matters is when you're trying
to debug something.  In that case, I'd say the onus is really
upon the debugger to tell you what kind of function it was.

Yes, this is my main concern I guess, as I use pretty plain
editors that tell me nothing. I rely on the code to tell me what
I need to know.

foo.bar();
foo.\bar();  // where \ means a free function

A different syntax for calling free functions would certainly
make it clearer (as the above demonstrates), but as you argue, it
would have a negative effect on encapsulation.

I guess with a more enhanced editor I could just mouse over UFCS
syntax, and it could identify a free function from a member
function. That would be nice, since there's no other way to know
without exploring code elsewhere...

I guess the days of use a plain text editor...are slowly coming
to and end ;-(

..what a shame...as I only just recently 'upgraded' from using vi
to using micro....

https://github.com/zyedidia/micro

Oct 31 2017
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Wed, Nov 01, 2017 at 03:38:32AM +0000, codephantom via Digitalmars-d wrote:
On Tuesday, 31 October 2017 at 15:45:42 UTC, H. S. Teoh wrote:
The one case where the difference matters is when you're trying to
debug something.  In that case, I'd say the onus is really upon the
debugger to tell you what kind of function it was.

Yes, this is my main concern I guess, as I use pretty plain editors
that tell me nothing. I rely on the code to tell me what I need to
know.

[...]
I guess with a more enhanced editor I could just mouse over UFCS
syntax, and it could identify a free function from a member function.
That would be nice, since there's no other way to know without
exploring code elsewhere...

I'm a vim user, and frankly, I don't find the need for "enhanced"
editors at all. (I don't even use syntax highlighting, but that's
another story. :P)

If there's a function call that could be either a member function or a
UFCS free function, all I need is to have the debugger print a
stacktrace and that ought to clear things up.  It will even resolve
other issues like telling me exactly which overload is being called, if
there are complicated overload sets, and point me to the exact file/line
of the code.  At that point, the difference between UFCS free function
or member function is basically irrelevant.

I guess the days of use a plain text editor...are slowly coming to and
end ;-(

[...]

Nope.  Plain text editors still rule.  GUIs are for wimps. :-P

T

--
The best way to destroy a cause is to defend it poorly.

Nov 01 2017
Dukc <ajieskola gmail.com> writes:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
For example, suppose you're using a proprietary library that
provides a class X that behaves pretty closely to a range, but
doesn't quite have a range API.  (Or any other API, really.)
Well, that's not a problem, you just write free functions that
forward to class X's methods to bridge the API gap, and off you
go.  You don't have to work with your upstream provider, who
may not be able to provide a fix until months later, and you
don't have to create all sorts of wrapper types just to adapt
one API to another.

Yes, the idea here is great. It will work with range functions
you define. The problem is, it won't work with Phobos functions
because they do not see your extensions to that proprietary type.
They see only the true member functions of it.

library there, which would be a very ugly solution.

You can of course wrap that proprietary range type but that is a
lot of manual work and requires maintenance. Alias this solves
some cases but not all of them.

I am not sure what would be the best way for the language to
handle this but for sure not the present way. The idiom is
otherwise so great.

Oct 31 2017
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Tue, Oct 31, 2017 at 08:45:11AM +0000, Dukc via Digitalmars-d wrote:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
For example, suppose you're using a proprietary library that
provides a class X that behaves pretty closely to a range, but
doesn't quite have a range API.  (Or any other API, really.)  Well,
that's not a problem, you just write free functions that forward to
class X's methods to bridge the API gap, and off you go.  You don't
have to work with your upstream provider, who may not be able to
provide a fix until months later, and you don't have to create all
sorts of wrapper types just to adapt one API to another.

Yes, the idea here is great. It will work with range functions you
define.  The problem is, it won't work with Phobos functions because
they do not see your extensions to that proprietary type. They see
only the true member functions of it.

library there, which would be a very ugly solution.

You can of course wrap that proprietary range type but that is a lot
of manual work and requires maintenance. Alias this solves some cases
but not all of them.

I am not sure what would be the best way for the language to handle
this but for sure not the present way. The idiom is otherwise so
great.

Haha, I knew somebody would bring this up.  I don't have a good solution
to this either.  The problem is that you can't export the type to Phobos
along with the additional UFCS stuff you tacked on to it.  I think
Phobos itself contains several hacks in order to work around this
problem for basic types, like importing std.array in generic code that
doesn't actually reference any arrays, but the import is necessary so
that arrays retain their range API.  Similar problems arise when you
pass user-defined types to Phobos where some methods are implemented as
UFCS.

The basic problem is that when a generic function in Phobos looks up a
method of type T, the lookup is done *in the scope of the Phobos
module*, not the caller's scope that passed in the T in the first place.
For example:

/* usertype.d */
module usertype;
struct UserType {
int memberMethod(Args...)(Args args);
}

/* ufcs.d */
module ufcs;
import usertype;
int ufcsMethod(Args...)(UserType u, Args args);

/* main.d */
module main;
import usertype, ufcs;
void main() {
UserType u;
int x, y, z;

// Lookup happens in module main, function main.  Since
// usertype.UserType is visible here, .memberMethod
// resolves to usertype.UserType.memberMethod.
u.memberMethod(x, y, z);

// Lookup happens in module main, function main.  Since
// ufcs.ufcsMethod is visible here, .ufcsMethod resolves
// to ufcs.memberMethod.
u.ufcsMethod(x, y, z);

// Calls Phobos function
u.find(x);
}

/* snippet of std.algorithm */
module std.algorithm;
...
HayStack find(HayStack, Needle)(HayStack h, Needle n) {
...
// Lookup happens in module std.algorithm. Since we
// don't have the import of module ufcs here,
// .ufcsMethod cannot be resolved.
h.ufcsMethod(n);
...
}

I wonder if there's an easy way to extend the language so that you can
specify which scope a function lookup will happen in.  Suppose,
hypothetically, we can specify a lookup to happen in the caller's
context.  Then UFCS would work:

/* snippet of std.algorithm */
module std.algorithm;
...
HayStack find(HayStack, Needle)(HayStack h, Needle n) {
...
// Hypothetical syntax:
with (HayStack.__callerContext)
h.ufcsMethod(n);
// Now lookup happens in the caller's context, i.e.,
// module main, function main.  Since module ufcs is
// visible there, this call resolves to ufcs.ufcsMethod.
...
}

The problem with this is that allowing the callee to access the context
of the caller opens up a can of worms w.r.t. symbol hijacking and
accessing local variables in the caller, which should be illegal.

Seems like the only workable solution in the current language is to wrap
the type in a custom type.  But like you said, that's high-maintenance,
and decreases encapsulation because when the implementation of UserType
changes, the wrapper type needs to change accordingly.  And the current
implementation of alias this is not without its own set of problems.

There *is* a generic wrapper type in Phobos that uses opDispatch for
member forwarding, but last time I checked, it also comes with its own
set of issues.  So currently, we still don't have a perfect solution for
this, even though we're so tantalizingly close.

T

--
Unix is my IDE. -- Justin Whear

Oct 31 2017
jmh530 <john.michael.hall gmail.com> writes:
On Monday, 30 October 2017 at 23:03:12 UTC, H. S. Teoh wrote:
Yeah, the whole "private is module-private, not
aggregate-private" throws a monkey wrench into the works.  I
can understand the logic behind module-private vs.
aggregate-private, but sometimes you really *do* want
aggregate-private, but D doesn't let you express that except
via splitting things up into submodules, which is a lot of