www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Weird template error

reply Brian <digitalmars brianguertin.com> writes:
I get this (minor) error using dmd 1.036, I don't know if it's been 
discovered or not.

test.d(20): Error: template foo!(int) is not a member of actor.world
test.d(20): Error: function expected before (), not 0 of type int

// And heres the code that causes it
class World {
	public void foo(T)() {
	}
	
}
class Actor {
	World _world;
	
	public World world() {
		return this._world;
	}
}

void main() {
	auto actor = new Actor;
	actor._world = new World;
	
	actor.world().foo!(int)(); // This works fine
	actor.world.foo!(int)(); // This causes the error
}
Nov 18 2008
next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Wed, Nov 19, 2008 at 12:14 AM, Brian <digitalmars brianguertin.com> wrote:
 I get this (minor) error using dmd 1.036, I don't know if it's been
 discovered or not.

 test.d(20): Error: template foo!(int) is not a member of actor.world
 test.d(20): Error: function expected before (), not 0 of type int

 // And heres the code that causes it
 class World {
        public void foo(T)() {
        }

 }
 class Actor {
        World _world;

        public World world() {
                return this._world;
        }
 }

 void main() {
        auto actor = new Actor;
        actor._world = new World;

        actor.world().foo!(int)(); // This works fine
        actor.world.foo!(int)(); // This causes the error
 }

This is just another example of the property sugar being subpar. With real properties, this would not happen. However, as it is, "actor.world.foo!(int)()" parses as an attempt to access "foo!(int)()" from the _method_ designated by "actor.world", _not_ from the return value of actor.world().
Nov 18 2008
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Jarrett Billingsley wrote:
 On Wed, Nov 19, 2008 at 12:14 AM, Brian <digitalmars brianguertin.com> wrote:
 I get this (minor) error using dmd 1.036, I don't know if it's been
 discovered or not.

 test.d(20): Error: template foo!(int) is not a member of actor.world
 test.d(20): Error: function expected before (), not 0 of type int

 // And heres the code that causes it
 class World {
        public void foo(T)() {
        }

 }
 class Actor {
        World _world;

        public World world() {
                return this._world;
        }
 }

 void main() {
        auto actor = new Actor;
        actor._world = new World;

        actor.world().foo!(int)(); // This works fine
        actor.world.foo!(int)(); // This causes the error
 }

This is just another example of the property sugar being subpar. With real properties, this would not happen. However, as it is, "actor.world.foo!(int)()" parses as an attempt to access "foo!(int)()" from the _method_ designated by "actor.world", _not_ from the return value of actor.world().

I think (in this particular case) it's only about a compiler bug. Andrei
Nov 19 2008
next sibling parent Janderson <ask me.com> writes:
Denis Koroskin wrote:

 But you are well aware of _other_ cases in which you _have to_ put an 
 extra pair of parens to access some property method/field thus broking 
 an encapsulation and preventing the class designer to replace properties 
 with fields and vice versa at a later stage.
 
 It's simply broken! You say that empty pair of parens is equivalent to 
 none of them and thus it is allowed to omit them, but it's not true at 
 all. There are lots of examples where "auto foo = bar();" != "auto foo = 
 bar;" and "auto foo = obj.bar();" != "auto foo = obj.bar;" (delegates, 
 class/struct instances with overloaded opCall, etc).
 
 - such a duality is confusing (when may you omit the parens and when you 
 may not?)
 - it makes the language more complex (rules are so complex that hardly 
 anyone fully understands them)
 - it leads to code inconsistency (half of the programmers remove an 
 "extra" pair of parens and other half preserve them)
 - it is a source of many compiler bugs (this and lots of related ones)
 - it contributes to user code bugs that are hard to find at times 
 ("oops, I missed a pair of parens. God, I thougth that they were 
 optional").
 
 IIRC, true property syntax was a #2 wish among the community according 
 to a recent "Top 5 D problems" poll and now that a Tango/Phobos common 
 runtime is implemented it becomes #1.
 
 I really wish I could understand Walter arguments against proper 
 properties in D...

Some good points. -Joel
Nov 19 2008
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Andrei Alexandrescu" wrote
 Jarrett Billingsley wrote:
 This is just another example of the property sugar being subpar.  With
 real properties, this would not happen.  However, as it is,
 "actor.world.foo!(int)()" parses as an attempt to access "foo!(int)()"
 from the _method_ designated by "actor.world", _not_ from the return
 value of actor.world().

I think (in this particular case) it's only about a compiler bug.

Yes, one that would not have existed without the ambiguity that is now present in the syntax. It is one of those things that Walter didn't forsee, but with true property syntax, it (and many other ambiguities) would have been covered. I'm not saying this is proof we should have property syntax, but I'm sure people will continue to find problems due to the inherent ambiguity. -Steve
Nov 20 2008
prev sibling next sibling parent reply Chad J <gamerchad __spam.is.bad__gmail.com> writes:
Denis Koroskin wrote:
...
 
 IIRC, true property syntax was a #2 wish among the community according 
 to a recent "Top 5 D problems" poll and now that a Tango/Phobos common 
 runtime is implemented it becomes #1.
 

Could I get a link to that one? I think I missed it, and I'm curious. Thanks, - Chad
Nov 20 2008
parent Gide Nwawudu <gide btinternet.com> writes:
On Thu, 20 Nov 2008 23:59:03 -0500, Chad J
<gamerchad __spam.is.bad__gmail.com> wrote:

Denis Koroskin wrote:
...
 
 IIRC, true property syntax was a #2 wish among the community according 
 to a recent "Top 5 D problems" poll and now that a Tango/Phobos common 
 runtime is implemented it becomes #1.
 

Could I get a link to that one? I think I missed it, and I'm curious.

http://www.digitalmars.com/d/archives/digitalmars/D/Top_5_77130.html Gide
Nov 21 2008
prev sibling next sibling parent reply Michel Fortin <michel.fortin michelf.com> writes:
On 2008-11-19 12:49:14 -0500, "Denis Koroskin" <2korden gmail.com> said:

 It's simply broken! You say that empty pair of parens is equivalent to  
 none of them and thus it is allowed to omit them, but it's not true at  
 all. There are lots of examples where "auto foo = bar();" != "auto foo 
 =  bar;" and "auto foo = obj.bar();" != "auto foo = obj.bar;" 
 (delegates,  class/struct instances with overloaded opCall, etc).

As much as I like the no-parens function call syntax, I have to agree with you: it bring inconsistencies.
 - such a duality is confusing (when may you omit the parens and when 
 you  may not?)

Where it becomes confusing is where you try to call the return value of a function, or a no-argument function template. While the most common case (simple function call) may be working right, the no-parens function call syntax creates many not-so-rare corner case we have to deal with. Those cases make it confusing.
 - it makes the language more complex (rules are so complex that hardly  
 anyone fully understands them)

I don't think each rule for each callable type is in itself that complex, but mixing them leads to complexity.
 - it leads to code inconsistency (half of the programmers remove an  
 "extra" pair of parens and other half preserve them)

Indeed. I don't think it's bad in itself to have two ways to write something. After all, you can call directly someStruct.opAdd if you prefer that to writing "+". The problem is that in some cases (function pointers, delegates, opCall) it changes the meaning while in other (plain function) it means the same thing. That's inconsistent, it prevents interchangability between those types, and it becomes confusing when using them together.
 - it is a source of many compiler bugs (this and lots of related ones)

I'm not sure having bugs in the compiler is a valid argument against the syntax. Creating a whole new property syntax is bound to have bugs too.
 - it contributes to user code bugs that are hard to find at times 
 ("oops,  I missed a pair of parens. God, I thougth that they were 
 optional").

Indeed. So as I said above, I agree that no-parens function calls make the language inconsistant. I still like it because I find it aesthetically pleasing and because it simplifies the concept of properties by making them simple function calls, but at the same time I'm quite annoyed by the inconsistencies. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Nov 21 2008
parent reply "Nick Sabalausky" <a a.a> writes:
"Michel Fortin" <michel.fortin michelf.com> wrote in message 
news:gg6c9q$1p2r$1 digitalmars.com...
 On 2008-11-19 12:49:14 -0500, "Denis Koroskin" <2korden gmail.com> said:

 It's simply broken! You say that empty pair of parens is equivalent to 
 none of them and thus it is allowed to omit them, but it's not true at 
 all. There are lots of examples where "auto foo = bar();" != "auto foo = 
 bar;" and "auto foo = obj.bar();" != "auto foo = obj.bar;" (delegates, 
 class/struct instances with overloaded opCall, etc).

As much as I like the no-parens function call syntax, I have to agree with you: it bring inconsistencies.
 - such a duality is confusing (when may you omit the parens and when you 
 may not?)

Where it becomes confusing is where you try to call the return value of a function, or a no-argument function template. While the most common case (simple function call) may be working right, the no-parens function call syntax creates many not-so-rare corner case we have to deal with. Those cases make it confusing.
 - it makes the language more complex (rules are so complex that hardly 
 anyone fully understands them)

I don't think each rule for each callable type is in itself that complex, but mixing them leads to complexity.
 - it leads to code inconsistency (half of the programmers remove an 
 "extra" pair of parens and other half preserve them)

Indeed. I don't think it's bad in itself to have two ways to write something. After all, you can call directly someStruct.opAdd if you prefer that to writing "+". The problem is that in some cases (function pointers, delegates, opCall) it changes the meaning while in other (plain function) it means the same thing. That's inconsistent, it prevents interchangability between those types, and it becomes confusing when using them together.

That's why I like the consistency other languages have of: - With parens: Invoke function - Without parens: Refer to function itself Example of preferred syntax: class Beeper { void beep() {...} } void main() { auto beeper = new Beeper(); // Beep beeper.beep(); // Make a beeping delegate auto dgBeep = beeper.beep; // Beep twice later doLater(dgBeep); doLater(beeper.beep); //Error: doLater expects "void delegate(void)", not "void" doLater(beeper.beep()); }
 - it is a source of many compiler bugs (this and lots of related ones)

I'm not sure having bugs in the compiler is a valid argument against the syntax. Creating a whole new property syntax is bound to have bugs too.
 - it contributes to user code bugs that are hard to find at times ("oops, 
 I missed a pair of parens. God, I thougth that they were optional").

Indeed. So as I said above, I agree that no-parens function calls make the language inconsistant. I still like it because I find it aesthetically pleasing and because it simplifies the concept of properties by making them simple function calls, but at the same time I'm quite annoyed by the inconsistencies. -- Michel Fortin michel.fortin michelf.com http://michelf.com/

Nov 25 2008
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky
 That's why I like the consistency other languages have of:
 - With parens: Invoke function
 - Without parens: Refer to function itself
 Example of preferred syntax:
 ...

I agree, this is better (in Python there's such syntax). (I am hopeful for the future of D because I've seen there are lot of people in this neswgroup that seem to have better ideas :-) ). In alternative, you may also require the & to refer to the function, this makes the syntax more explicit and makes the compiler raise an error if you forget both parens or &: auto x = foo(); // call the callable auto x = &foo; // delegate or function pointer or closure auto x = foo; // syntax error Bye, bearophile
Nov 25 2008
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Nick Sabalausky Wrote:
 That's why I like the consistency other languages have of:
 - With parens: Invoke function
 - Without parens: Refer to function itself

It sounds nice, but then this is what may happen: http://groups.google.com/group/comp.lang.python/browse_thread/thread/1173059b4c786e4b# That's why I have suggested a more explicit syntax, to avoid mistakes, that only adds a char (&): auto x = foo(); // call the callable auto x = &foo; // delegate or function pointer or closure auto x = foo.sizeof; // OK, x becomes 4 or 8, etc. auto x = foo; // syntax error Bye, bearophile
Nov 27 2008
prev sibling parent "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Wed, Nov 26, 2008 at 4:07 PM, Nick Sabalausky <a a.a> wrote:
 But how often are "inout" params really used anyway?

I use them a lot when passing nontrivial structs around.
 Plus, unless there's
 dynamic library loading going on, it would only have to generate the ones
 that are actually used.

No; the problem manifests itself whenever separate compilation is used as well, including not only compiling normal programs, but also using statically-linked libraries.
Nov 26 2008
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Wed, 19 Nov 2008 19:44:38 +0300, Andrei Alexandrescu  
<SeeWebsiteForEmail erdani.org> wrote:

 Jarrett Billingsley wrote:
 On Wed, Nov 19, 2008 at 12:14 AM, Brian <digitalmars brianguertin.com>  
 wrote:
 I get this (minor) error using dmd 1.036, I don't know if it's been
 discovered or not.

 test.d(20): Error: template foo!(int) is not a member of actor.world
 test.d(20): Error: function expected before (), not 0 of type int

 // And heres the code that causes it
 class World {
        public void foo(T)() {
        }

 }
 class Actor {
        World _world;

        public World world() {
                return this._world;
        }
 }

 void main() {
        auto actor = new Actor;
        actor._world = new World;

        actor.world().foo!(int)(); // This works fine
        actor.world.foo!(int)(); // This causes the error
 }

real properties, this would not happen. However, as it is, "actor.world.foo!(int)()" parses as an attempt to access "foo!(int)()" from the _method_ designated by "actor.world", _not_ from the return value of actor.world().

I think (in this particular case) it's only about a compiler bug. Andrei

But you are well aware of _other_ cases in which you _have to_ put an extra pair of parens to access some property method/field thus broking an encapsulation and preventing the class designer to replace properties with fields and vice versa at a later stage. It's simply broken! You say that empty pair of parens is equivalent to none of them and thus it is allowed to omit them, but it's not true at all. There are lots of examples where "auto foo = bar();" != "auto foo = bar;" and "auto foo = obj.bar();" != "auto foo = obj.bar;" (delegates, class/struct instances with overloaded opCall, etc). - such a duality is confusing (when may you omit the parens and when you may not?) - it makes the language more complex (rules are so complex that hardly anyone fully understands them) - it leads to code inconsistency (half of the programmers remove an "extra" pair of parens and other half preserve them) - it is a source of many compiler bugs (this and lots of related ones) - it contributes to user code bugs that are hard to find at times ("oops, I missed a pair of parens. God, I thougth that they were optional"). IIRC, true property syntax was a #2 wish among the community according to a recent "Top 5 D problems" poll and now that a Tango/Phobos common runtime is implemented it becomes #1. I really wish I could understand Walter arguments against proper properties in D...
Nov 19 2008
prev sibling next sibling parent Brian <digitalmars brianguertin.com> writes:
I don't understand why we wouldn't want properties either.  Another issue 
I've had a couple times, although there might be a good reason for this 
I'm not aware of:

class Foo {
	int x;
}

void set(inout int x) {
	x = 10;
}

void main() {
	auto foo = new Foo();
	set(foo.x); // This works fine
}

But then if you need to make x a property:

class Foo {
	int _x;
	
	int x() { return _x; }
	int x(int val) { return _x = val; }
}

You get "Error: foo.x() is not an lvalue"
Nov 19 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Wed, Nov 19, 2008 at 4:59 PM, Brian <digitalmars brianguertin.com> wrote:
 I don't understand why we wouldn't want properties either.  Another issue
 I've had a couple times, although there might be a good reason for this
 I'm not aware of:

 class Foo {
        int x;
 }

 void set(inout int x) {
        x = 10;
 }

 void main() {
        auto foo = new Foo();
        set(foo.x); // This works fine
 }

 But then if you need to make x a property:

 class Foo {
        int _x;

        int x() { return _x; }
        int x(int val) { return _x = val; }
 }

 You get "Error: foo.x() is not an lvalue"

That one's trickier, even if you did have properties. Even "true" properties would still boil down to a function call. Set expects a reference to an integer. How do you convert getter/setter functions into an integer? It doesn't work now because foo.x is a function, not an int. Also, I wonder if ref returns could help here..
Nov 19 2008
parent "Nick Sabalausky" <a a.a> writes:
"Jarrett Billingsley" <jarrett.billingsley gmail.com> wrote in message 
news:mailman.19.1227139335.22690.digitalmars-d puremagic.com...
 On Wed, Nov 19, 2008 at 4:59 PM, Brian <digitalmars brianguertin.com> 
 wrote:
 I don't understand why we wouldn't want properties either.  Another issue
 I've had a couple times, although there might be a good reason for this
 I'm not aware of:

 class Foo {
        int x;
 }

 void set(inout int x) {
        x = 10;
 }

 void main() {
        auto foo = new Foo();
        set(foo.x); // This works fine
 }

 But then if you need to make x a property:

 class Foo {
        int _x;

        int x() { return _x; }
        int x(int val) { return _x = val; }
 }

 You get "Error: foo.x() is not an lvalue"

That one's trickier, even if you did have properties. Even "true" properties would still boil down to a function call. Set expects a reference to an integer. How do you convert getter/setter functions into an integer?

class Foo { // This syntax is a modified version of C#'s properties int x { get { return x.rawValue; }; set { x.rawValue = newValue; } } } void makeTen(inout int x) { x = 10; } void main() { int i; auto foo = new Foo(); makeTen(i); // Ok // compiler knows that foo.x is an int property // and turns this: makeTen(foo.x); // into this: auto _foo_x = foo.x; makeTen(_foo_x); foo.x = _foo_x; // and finally this: auto _foo_x = foo.x.get(); makeTen(_foo_x); foo.x.set(_foo_x); }
 It doesn't work now because foo.x is a function, not an int.

 Also, I wonder if ref returns could help here.. 

Nov 25 2008
prev sibling next sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Tue, Nov 25, 2008 at 10:44 PM, Nick Sabalausky <a a.a> wrote:
       // compiler knows that foo.x is an int property
       // and turns this:
       makeTen(foo.x);

       // into this:
       auto _foo_x = foo.x;
       makeTen(_foo_x);
       foo.x = _foo_x;

       // and finally this:
       auto _foo_x = foo.x.get();
       makeTen(_foo_x);
       foo.x.set(_foo_x);

I considered this, but what if your setter/getter had some kind of side effect? I know, it's probably a little thing to worry about, but still, I would have intuitively expected it to have been evaluated each time the ref parameter was accessed in the function instead of just once before and after the call.
Nov 25 2008
parent "Nick Sabalausky" <a a.a> writes:
"Jarrett Billingsley" <jarrett.billingsley gmail.com> wrote in message 
news:mailman.59.1227675354.22690.digitalmars-d puremagic.com...
 On Tue, Nov 25, 2008 at 10:44 PM, Nick Sabalausky <a a.a> wrote:
       // compiler knows that foo.x is an int property
       // and turns this:
       makeTen(foo.x);

       // into this:
       auto _foo_x = foo.x;
       makeTen(_foo_x);
       foo.x = _foo_x;

       // and finally this:
       auto _foo_x = foo.x.get();
       makeTen(_foo_x);
       foo.x.set(_foo_x);

I considered this, but what if your setter/getter had some kind of side effect? I know, it's probably a little thing to worry about, but still, I would have intuitively expected it to have been evaluated each time the ref parameter was accessed in the function instead of just once before and after the call.

I thought about that too, but the only realistic examples of that I can think of would involve writing getters/setters that abuse the whole point of property syntax. I'm not completely certain, but I think this might be an issue that's akin to using operator overloading to make '*' peform a subtraction. Can anyone think of any non-abusive case where the above would fail? Maybe something with threads and synchronization? Another idea, but possibly messy: Maybe there could be some automatic behind-the-scenes templatization/overloading such that: If there's a function ("bar") that has a parameter passed by reference ("x"), and you try to pass "bar" a property (of the correct type), then an overloaded version of "bar" is created which replaces accesses to x with calls to x's getter/setter. Ie: void bar(inout int x) { x = x + 2; } void main() { int i; auto foo = new Foo(); bar(i); bar(foo.x); // When the compiler detects the above line, // it generates the following overload of bar: } // Automatically generated by compiler, // unless "bar(foo.x);" above is commented out. void bar(inout property!(int) x) // "property!(T)" is either built-in or part of the core library { x.setter(x.getter() + 2); } Although, I suppose that might still create an excess of extra functions when using dynamically-linked libraries (or maybe not, because it would probably only be needed on inout params, and I don't think it's common to have a large number of those).
Nov 26 2008
prev sibling parent reply "Jarrett Billingsley" <jarrett.billingsley gmail.com> writes:
On Wed, Nov 26, 2008 at 8:12 AM, Nick Sabalausky <a a.a> wrote:
 Another idea, but possibly messy:
 Maybe there could be some automatic behind-the-scenes
 templatization/overloading such that: If there's a function ("bar") that has
 a parameter passed by reference ("x"), and you try to pass "bar" a property
 (of the correct type), then an overloaded version of "bar" is created which
 replaces accesses to x with calls to x's getter/setter.

 Ie:

 void bar(inout int x)
 {
    x = x + 2;
 }

 void main()
 {
   int i;
   auto foo = new Foo();
   bar(i);

   bar(foo.x);
   // When the compiler detects the above line,
   // it generates the following overload of bar:
 }

 // Automatically generated by compiler,
 // unless "bar(foo.x);" above is commented out.
 void bar(inout property!(int) x)  // "property!(T)" is either built-in or
 part of the core library
 {
    x.setter(x.getter() + 2);
 }

 Although, I suppose that might still create an excess of extra functions
 when using dynamically-linked libraries (or maybe not, because it would
 probably only be needed on inout params, and I don't think it's common to
 have a large number of those).

It would also make it difficult to have virtual methods with ref parameters, since the compiler might have to generate 2 ^ (num ref params) overloads of each such method.
Nov 26 2008
parent "Nick Sabalausky" <a a.a> writes:
"Jarrett Billingsley" <jarrett.billingsley gmail.com> wrote in message 
news:mailman.61.1227711020.22690.digitalmars-d puremagic.com...
 On Wed, Nov 26, 2008 at 8:12 AM, Nick Sabalausky <a a.a> wrote:
 Another idea, but possibly messy:
 Maybe there could be some automatic behind-the-scenes
 templatization/overloading such that: If there's a function ("bar") that 
 has
 a parameter passed by reference ("x"), and you try to pass "bar" a 
 property
 (of the correct type), then an overloaded version of "bar" is created 
 which
 replaces accesses to x with calls to x's getter/setter.

 Ie:

 void bar(inout int x)
 {
    x = x + 2;
 }

 void main()
 {
   int i;
   auto foo = new Foo();
   bar(i);

   bar(foo.x);
   // When the compiler detects the above line,
   // it generates the following overload of bar:
 }

 // Automatically generated by compiler,
 // unless "bar(foo.x);" above is commented out.
 void bar(inout property!(int) x)  // "property!(T)" is either built-in or
 part of the core library
 {
    x.setter(x.getter() + 2);
 }

 Although, I suppose that might still create an excess of extra functions
 when using dynamically-linked libraries (or maybe not, because it would
 probably only be needed on inout params, and I don't think it's common to
 have a large number of those).

It would also make it difficult to have virtual methods with ref parameters, since the compiler might have to generate 2 ^ (num ref params) overloads of each such method.

But how often are "inout" params really used anyway? Plus, unless there's dynamic library loading going on, it would only have to generate the ones that are actually used. I still prefer my original suggestion though. This is just in the case that my original suggestion turns out to have problems that realistically crop up even when not abusing property syntax.
Nov 26 2008