April 21, 2014
On Mon, 21 Apr 2014 09:46:18 -0400, Lars T. Kyllingstad <public@kyllingen.net> wrote:

> On Monday, 21 April 2014 at 12:45:12 UTC, Steven Schveighoffer wrote:

>> 3. There is zero chance of a conflict with another type's similarly named method.
>
> How?  If you have the following functions:
>
>      void foo(A a);
>      void foo(B b);
>
> and you write
>
>      foo(new B);
>
> there is also zero chance of conflict -- even if B happens to be a subclass of A, since the most specialised function is always called.

I definitely restricted myself too much when I said "another type." Yes, there is a very low possibility of A and B conflicting. But as I showed in the other post, there is the possibility of confusing the compiler when calling a UFCS method.

Essentially, the core issue is that a type provides the strongest tie to its method overload set. The module's overload set has looser ties, so they can be accidentally (or intentionally) overridden. It was a common con against UFCS before it was introduced.

-Steve
April 21, 2014
On Monday, 21 April 2014 at 14:10:08 UTC, Steven Schveighoffer wrote:
> [...]
>
> module m1;
> import std.stdio;
>
> class C {}
>
> void foo(C c)
> {
>    writeln("C.foo");
> }
>
> void bar(C c)
> {
>    writeln("C.bar");
> }
>
> module m2;
> import m1;
> import std.stdio;
>
> void foo(T)(T t)
> {
>   writeln("m2.foo");
> }
>
> void bar(T)(T t, int x)
> {
>   writeln("m2.bar");
> }
>
> void main()
> {
>    auto c = new C;
>    c.foo(); // "m2.foo";
>    //c.bar(); // error if uncommented!
> }
>
> Basically, I've inadvertently overridden C.foo, without intending to. With bar, I've somehow hidden the inherent functionality of C!

Wow, I didn't know that.  I thought the most specialised function would always be selected, regardless of which module it's defined in.  What you've demonstrated feels wrong, somehow.

>>> 4. It enforces the "method call" syntax. I.e. you cannot use foo(obj) call. This may be important for readability.
>>
>> Some would argue that giving users the choice between typing foo(obj) and obj.foo() is a Good Thing, because it doesn't impose your preferences on them.  I'm not going to do that, though. ;)
>
> You may recall that I am a big proponent of explicit properties because I think the ways of calling functions have strong implications to the reader, regardless of the functions. This is the same thing. I look at foo(x) much differently than x.foo().

And you may recall that I was on the same side as you in the properties debate, though less vocal about it.  (In fact, I think we didn't go far enough with properties -- we should also forbid taking their address.  But that's another discussion.)  The point is, I lean towards the same view as you when it comes to UFCS, and only brought up the opposing view for the sake of the discussion.

I tend to use UFCS only for a few select cases (array range functions, range chaining, etc.), and to otherwise use the "normal" function call syntax.  If someone sees this in my code:

  obj.foo();

I want them to know where to look for further information about foo(), namely in the class documentation/code.
April 21, 2014
On Monday, 21 April 2014 at 14:38:53 UTC, Lars T. Kyllingstad wrote:
> What you've demonstrated feels wrong, somehow.

As in "it shouldn't be that way", not as in "I think you're wrong about this". :)
April 21, 2014
On Monday, 21 April 2014 at 14:10:08 UTC, Steven Schveighoffer wrote:
> You may recall that I am a big proponent of explicit properties because I think the ways of calling functions have strong implications to the reader, regardless of the functions. This is the same thing. I look at foo(x) much differently than x.foo().

I agree. What was the reasoning by conflating the two?

I only see disadvantages:
- harder to read
- reduced namespace
- possibility of breaking application code when adding members to libraries?
April 21, 2014
On 4/21/14, 1:49 AM, Lars T. Kyllingstad wrote:
> On Sunday, 20 April 2014 at 20:36:58 UTC, Andrei Alexandrescu wrote:
>> On 4/20/14, 12:11 AM, Lars T. Kyllingstad wrote:
>>> The fact that "private" really means "module private" in D means that
>>> any number of functions can break when a class/struct implementation
>>> changes.
>>
>> No, only those in that module. There's no change. -- Andrei
>
> Ok, so "any number" was poorly phrased.  What I meant was "a large
> number", because in my experience, modules tend to be quite large.

The point here is boundedness, i.e. whether stuff that's affected is within your control or not. The module you're working on is trivially "yours". Changes visible outside the module are "unbounded" because they affect present and future client code.

> I often wish "private" meant class private in D.

I think we're in good shape here.


Andrei

April 21, 2014
On Mon, 21 Apr 2014 11:02:14 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote:

> On 4/21/14, 1:49 AM, Lars T. Kyllingstad wrote:
>> On Sunday, 20 April 2014 at 20:36:58 UTC, Andrei Alexandrescu wrote:
>>> On 4/20/14, 12:11 AM, Lars T. Kyllingstad wrote:
>>>> The fact that "private" really means "module private" in D means that
>>>> any number of functions can break when a class/struct implementation
>>>> changes.
>>>
>>> No, only those in that module. There's no change. -- Andrei
>>
>> Ok, so "any number" was poorly phrased.  What I meant was "a large
>> number", because in my experience, modules tend to be quite large.
>
> The point here is boundedness, i.e. whether stuff that's affected is within your control or not. The module you're working on is trivially "yours". Changes visible outside the module are "unbounded" because they affect present and future client code.
>
>> I often wish "private" meant class private in D.
>
> I think we're in good shape here.

Sure, but Lars' point that it completely precludes the encapsulation mechanism that Scott is advocating, is true. You would have to put the functions outside the core module to give them the same isolation as non-friend C++ global functions.

Note, I'm with you that the current mechanism is the "right way." It just completely prevents that technique of encapsulation :)

-Steve
April 21, 2014
On 4/21/14, 8:08 AM, Steven Schveighoffer wrote:
> Sure, but Lars' point that it completely precludes the encapsulation
> mechanism that Scott is advocating, is true. You would have to put the
> functions outside the core module to give them the same isolation as
> non-friend C++ global functions.
>
> Note, I'm with you that the current mechanism is the "right way." It
> just completely prevents that technique of encapsulation :)
>
> -Steve

Got it, thanks. -- Andrei
April 21, 2014
On 04/21/14 14:45, Steven Schveighoffer via Digitalmars-d wrote:
> Reasons off the top of my head not to make them module functions:

[...]

Functions, unlike methods, do not work with rvalues.

Ie

   struct S {
      long[999999] data;
      auto f() { return data[0]; }
   }

   auto g(ref S _this) { with (_this) return data[1]; }

   void main() {
      auto a = S().f();
      auto b = S().g();
   }

artur
April 21, 2014
On Monday, 21 April 2014 at 16:35:23 UTC, Artur Skawina via Digitalmars-d wrote:
> On 04/21/14 14:45, Steven Schveighoffer via Digitalmars-d wrote:
>> Reasons off the top of my head not to make them module functions:
>
> [...]
>
> Functions, unlike methods, do not work with rvalues.
>
> Ie
>
>    struct S {
>       long[999999] data;
>       auto f() { return data[0]; }
>    }
>
>    auto g(ref S _this) { with (_this) return data[1]; }
>
>    void main() {
>       auto a = S().f();
>       auto b = S().g();
>    }
>
> artur

Shouldn't this be possible if you want to make g a template function and use auto ref? Regardless, it doesn't. Probably a compiler bug:

  struct S {
     long[999999] data;
     auto f() { return data[0]; }
  }

  //No good
  //auto g(T: S)(auto ref T _this) { with (_this) return data[1]; }

  //Doesn't work either
  //auto g()(auto ref S _this) { with (_this) return data[1]; }

  void main() {
     auto a = S().f();
     auto b = S().g();
  }
April 22, 2014
On 21/04/14 10:49, Lars T. Kyllingstad wrote:

> Ok, so "any number" was poorly phrased.  What I meant was "a large
> number", because in my experience, modules tend to be quite large.
> Specifically, they are rarely limited to containing just a single
> class.  They often contain multiple classes, along with most related
> functionality.  In principle, changing the implementation of one class
> can break the implementation of another class!  Now, you may argue that
> kitchen sink modules are poor programming style, but it seems to be a
> common style, with Phobos being a very prominent example. :)

Phobos is a very bad example of code organizing. I'm almost exclusively organizing with one class per module and a deeper hierarchy of packages. Not saying that is the ideal solution.

-- 
/Jacob Carlborg