April 29, 2014
On Tuesday, 29 April 2014 at 20:33:07 UTC, Walter Bright wrote:
> Not really. It is reasonable to expect that when Framework1 and Framework2 each import 4th party Vec, that they do it with an import rather than inlining Vec's declarations.

Vectors are not the best example since one library might use struct{float x,y,z,w;} and another use float[4], but that would be too complicated to unify on the D side without a wrapper. At least as far as I can tell.

(I.e. the C++ frameworks interoperate because the memory layout and semantics are the same, although the types are not.)

>> With "graphics::g(physics::f(…))" this would not have been an issue.
>
> Having an alternative lookup method is a large increase in language complexity.

I wouldn't mind using fully qualified names, but others probably would.

>It does occur to me that two C++ symbols in the same namespace may be regarded as the same by the lookup code. That may be a reasonable enhancement request.

Yes, that would help.
April 29, 2014
On 04/29/2014 10:49 PM, Steven Schveighoffer wrote:
> On Tue, 29 Apr 2014 16:00:43 -0400, Steven Schveighoffer
> <schveiguy@yahoo.com> wrote:
>
>> On Tue, 29 Apr 2014 15:52:01 -0400, Walter Bright
>> <newshound2@digitalmars.com> wrote:
>
>>> Because the compiler would now issue an error for that, it's its
>>> anti-hijacking feature.
>>>
>>> Try it and see!
>>
>> I agree! that was my central point, which Timon seemed to be arguing
>> against :)
>
> And in fact, so were you!
> ...
>
> This is EXACTLY the same code that you said would now be an error above!

Maybe he didn't notice that you changed the 'main' function relative to my post. If you don't mention 'foo' explicitly, then obviously it cannot be hidden by the import and the code is in error.

>
> I think you guys  need to reexamine this,

Not me. I typically test my claims even if I am sure, if only to file bug reports.

> and choose one way or another.
> At this point, I have no clue as to how it's supposed to work.
>

Obviously you did not actually try. :P

Again, to make it really easy for you to test the behaviour:

module foo;
import std.stdio;

int func(){ writeln("hello from foo!"); return 1; }

//---

module bar;
import std.stdio;

mixin template X(){
    int func(){ writeln("hello from bar!"); return 2; }
}
mixin X foo;

//---

module prog;

void main(){
    void onlybar(){
        import bar;
        auto r=foo.func(); // hello from bar!
        assert(r==2); // pass!
    }
    void fooandbar(){
        import bar,foo;
        auto r=foo.func(); // hello from foo!
        assert(r==1); // pass!
    }
    onlybar();
    fooandbar();
}


http://dlang.org/download.html

$ dmd prog foo bar && ./prog
hello from bar!
hello from foo!

This is because the import of module 'foo' hides the namespace 'foo' imported from 'bar' in the scope of 'fooandbar'. It is not 'func' that is being hidden, but 'foo'.

April 29, 2014
On 4/29/14, 12:49 PM, Walter Bright wrote:
> On 4/29/2014 8:43 AM, Timon Gehr wrote:
>> as the DIP _does not actually introduce any new lookup rules_
>>  [...]
>> In particular, any problems you find with symbol lookup are actually
>> orthogonal
>> to the DIP.
>
> Yes!

This is a biggie. KISS etc. -- Andrei
April 29, 2014
On Monday, 28 April 2014 at 01:18:04 UTC, Walter Bright wrote:
>> This is the new grammar?
>>
>> LinkageAttribute:
>>     'extern' '(' identifier '++'? (',' identifier)? ')'
>
> You can also have N.M

I've updated the DIP page to include documentation of the change to the language grammar.
April 30, 2014
On 4/29/2014 4:08 PM, Brian Schott wrote:
> I've updated the DIP page to include documentation of the change to the language
> grammar.

thanks!
April 30, 2014
On Tue, 29 Apr 2014 17:38:07 -0400, Timon Gehr <timon.gehr@gmx.ch> wrote:

> On 04/29/2014 10:49 PM, Steven Schveighoffer wrote:
>> On Tue, 29 Apr 2014 16:00:43 -0400, Steven Schveighoffer
>> <schveiguy@yahoo.com> wrote:
>>
>>> On Tue, 29 Apr 2014 15:52:01 -0400, Walter Bright
>>> <newshound2@digitalmars.com> wrote:
>>
>>>> Because the compiler would now issue an error for that, it's its
>>>> anti-hijacking feature.
>>>>
>>>> Try it and see!
>>>
>>> I agree! that was my central point, which Timon seemed to be arguing
>>> against :)
>>
>> And in fact, so were you!
>> ...
>>
>> This is EXACTLY the same code that you said would now be an error above!
>
> Maybe he didn't notice that you changed the 'main' function relative to my post. If you don't mention 'foo' explicitly, then obviously it cannot be hidden by the import and the code is in error.

I never changed the code. It always was foo.func(). That was my point.

>
>>
>> I think you guys  need to reexamine this,
>
> Not me. I typically test my claims even if I am sure, if only to file bug reports.

Your view has been consistent. I think it defeats the purpose of having namespaces to disambiguate calls, since the disambiguation itself conflicts with modules.

If foo.func means one thing today and another thing tomorrow, based on an import, I think the feature is flawed. Template namespaces are just as flawed.

>
>> and choose one way or another.
>> At this point, I have no clue as to how it's supposed to work.
>>
>
> Obviously you did not actually try. :P

No, I get what you are saying. The above statement is because I'm getting conflicting reports from Walter.

> Again, to make it really easy for you to test the behaviour:
>
> module foo;
> import std.stdio;
>
> int func(){ writeln("hello from foo!"); return 1; }
>
> //---
>
> module bar;
> import std.stdio;
>
> mixin template X(){
>      int func(){ writeln("hello from bar!"); return 2; }
> }
> mixin X foo;
>
> //---
>
> module prog;
>
> void main(){
>      void onlybar(){
>          import bar;
>          auto r=foo.func(); // hello from bar!
>          assert(r==2); // pass!
>      }
>      void fooandbar(){
>          import bar,foo;
>          auto r=foo.func(); // hello from foo!
>          assert(r==1); // pass!
>      }
>      onlybar();
>      fooandbar();
> }

Wouldn't a similar test be to create a struct for a namespace?

The confusing issue here to C++ programmers is, when I specify x::y::z, it means z in namespace x::y, regardless of where it was imported. If in D we say you can access this via x.y.z, they are going to think they can always type that. To have it so easily break is not a good thing.

>
>
> http://dlang.org/download.html
>
> $ dmd prog foo bar && ./prog
> hello from bar!
> hello from foo!
>
> This is because the import of module 'foo' hides the namespace 'foo' imported from 'bar' in the scope of 'fooandbar'. It is not 'func' that is being hidden, but 'foo'.

In fact, it's the entire foo namespace.

module bar;

import std.stdio;

mixin template X(){
    int func(){ writeln("hello from bar!"); return 2; }
    int func2(){ writeln("hello also from bar!"); return 2;} // no equivalent in foo.d
}
mixin X foo;

module foo; // same as before
...

module prog;
import foo; // comment out to compile
import bar;

void main()
{
   foo.func2(); // Error: undefined identifier 'func2', did you mean 'function func'?
}

So basically any namespace that matches the root phobos import path will cause conflicts. You don't suppose any C++ code uses that do you? ;)

-Steve
April 30, 2014
On Tuesday, 29 April 2014 at 22:51:42 UTC, Andrei Alexandrescu wrote:
> On 4/29/14, 12:49 PM, Walter Bright wrote:
>> On 4/29/2014 8:43 AM, Timon Gehr wrote:
>>> as the DIP _does not actually introduce any new lookup rules_
>>> [...]
>>> In particular, any problems you find with symbol lookup are actually
>>> orthogonal
>>> to the DIP.
>>
>> Yes!
>
> This is a biggie. KISS etc. -- Andrei

KISS: keep 'em seperate

!KISS: map language 1 onto language 2

Wrong KISS: compiler internals over specification
April 30, 2014
On Wed, 30 Apr 2014 05:03:58 +0100, Ola Fosheim Grøstad <ola.fosheim.grostad+dlang@gmail.com> wrote:
> Wrong KISS: compiler internals over specification

Indeed.

I've been a C/C++ developer for ~16 years and I was confused several times reading this thread.

The mix of D modules and C++ namespaces is the thing what needs to be kept simple for us lesser mortals, not the compiler implementation - which should, I agree, *ideally* remain simple, but in this case should be sacrificed for the other because compiler writers are good at what they do and will be able to cope.

I think it is simpler all round to just invent (and perhaps reserve) a new top level module for C++ namespaces (an idea mentioned here already) i.e. "cpp"

Example:

module a;
extern(C++, std) class string {..} // identical to decl in b

module b:
extern(C++, std) class string {..} // identical to decl in a
extern(C++, std) class vector {..} // new decl

module userland;
import a;
import b;

void main()
{
  cpp.std.string x = new cpp.std.string();
  cpp.std.vector y = new cpp.std.vector();
}

Notes:
 - the D modules 'a' and 'b' play no part whatsoever in the lookup of the C++ symbol (why the hell should they? I see no benefit to this)
 - the identical declarations in a/b for std.string are treated as one.
 - any *use* (in userland) of a non-identical/ambiguous declaration would result in an error.

Link time is where it would actually complain if multiple C++ symbols were found.

Special lookup rules would apply to cpp.*

My 2p/c

R

-- 
Using Opera's revolutionary email client: http://www.opera.com/mail/
April 30, 2014
On Wed, 30 Apr 2014 10:20:22 +0100, Regan Heath <regan@netmail.co.nz> wrote:

Something else to think about.

C# has the same problem and has solved it the following way..

[main.cs]
using ..
using CSTest_Test1;
using CSTest_Test2;

namespace CSTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Test1.GetLastError(); // class, not namespace required to call method
            Test2.GetLastError(); // class, not namespace required to call method
        }
    }
}

[Test1.cs]
using ..
namespace CSTest_Test1
{
    public static class Test1
    {
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern Int32 GetLastError();
    }
}

[Test2.cs]
namespace CSTest_Test2
{
    public static class Test2
    {
        [DllImport("coredll.dll", SetLastError = true)]
        public static extern Int32 GetLastError();
    }
}

GetLastError() is always going to unambiguous here because it *must* live inside a C# class and that class name is *always* required in the call to it.

If D has replaced classes/namespaces with modules, then the answer to our problem may be to use the C++ namespace *only* to mangle the symbol, and *only* use the D module for lookup resolution.

module a;
extern(C++, std) class string {..}

module b:
extern(C++, std) class string {..}
extern(C++, std) class vector {..}

module userland;
import a;
import b;

void main()
{
  string x = new string();     //error ambiguous (same resolution as for D symbols)
  a.string x = new a.string(); //ok
  b.vector y = new b.vector(); //ok
}

Regan
April 30, 2014
On 04/30/2014 02:41 AM, Steven Schveighoffer wrote:
> On Tue, 29 Apr 2014 17:38:07 -0400, Timon Gehr <timon.gehr@gmx.ch> wrote:
> ...
>>
>> Maybe he didn't notice that you changed the 'main' function relative
>> to my post. If you don't mention 'foo' explicitly, then obviously it
>> cannot be hidden by the import and the code is in error.
>
> I never changed the code. It always was foo.func(). That was my point.
> ...

(What I mean is that in the post you were answering, main just contained 'foo'.)

>>
>>>
>>> I think you guys  need to reexamine this,
>>
>> Not me. I typically test my claims even if I am sure, if only to file
>> bug reports.
>
> Your view has been consistent. I think it defeats the purpose of having
> namespaces to disambiguate calls, since the disambiguation itself
> conflicts with modules.
>
> If foo.func means one thing today and another thing tomorrow, based on
> an import, I think the feature is flawed. Template namespaces are just
> as flawed.
>
>>
>>> and choose one way or another.
>>> At this point, I have no clue as to how it's supposed to work.
>>>
>>
>> Obviously you did not actually try. :P
>
> No, I get what you are saying. The above statement is because I'm
> getting conflicting reports from Walter.
> ...

Ok, apologies.

>> ...
>>
>> module foo;
>> import std.stdio;
>>
>> int func(){ writeln("hello from foo!"); return 1; }
>>
>> //---
>>
>> module bar;
>> import std.stdio;
>>
>> mixin template X(){
>>      int func(){ writeln("hello from bar!"); return 2; }
>> }
>> mixin X foo;
>>
>> //---
>>
>> module prog;
>>
>> void main(){
>>      void onlybar(){
>>          import bar;
>>          auto r=foo.func(); // hello from bar!
>>          assert(r==2); // pass!
>>      }
>>      void fooandbar(){
>>          import bar,foo;
>>          auto r=foo.func(); // hello from foo!
>>          assert(r==1); // pass!
>>      }
>>      onlybar();
>>      fooandbar();
>> }
>
> Wouldn't a similar test be to create a struct for a namespace?
> ...

Yes, and this behaves the same in this specific case.

> The confusing issue here to C++ programmers is, when I specify x::y::z,
> it means z in namespace x::y, regardless of where it was imported. If in
> D we say you can access this via x.y.z, they are going to think they can
> always type that. To have it so easily break is not a good thing.
> ...

If this is a problem, I guess the most obvious alternatives are to:

1. Get rid of namespace scopes. Require workarounds in the case of conflicting definitions in different namespaces in the same file. (Eg. use a mixin template.) I'd presume this would not happen often.

2. Give the global C++ namespace a distinctive name and put all other C++ namespaces below it. This way fully qualified name lookup will be reliable.

>>
>> This is because the import of module 'foo' hides the namespace 'foo'
>> imported from 'bar' in the scope of 'fooandbar'. It is not 'func' that
>> is being hidden, but 'foo'.
>
> In fact, it's the entire foo namespace.
>...

Yes, but this is visible in the file that is being changed.

> So basically any namespace that matches the root phobos import path will
> cause conflicts. You don't suppose any C++ code uses that do you? ;)
>

How many C++ standard headers contain conflicting symbol names in different namespaces? If the namespace symbol really needs to be addressable, there could be an alias. eg. alias cpp=std;