www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D's "accessors" are like abusing operator overloads

reply "Nick Sabalausky" <a a.a> writes:
I don't mean to put words in the Tango crew's mouths, but IMO this piece 
from 0.99.8's changelog is a great demonstration of what's wrong with D's 
sloppy "accessor" syntax:

-------------
tango.time

* TimeSpan.seconds(ulong), etc. is now replaced by 
TimeSpan.fromSeconds(ulong), etc. The original form causes problems when 
people write code like:

auto ts = TimeSpan.seconds(60);
ts.seconds = 30; // you would think this would assign 30 seconds to ts, but 
what it does is create a
                 // temporary and throw it away.
-------------

The fact that D lets you freely switch between "foo(x)" and "foo = x" (at 
least when you're not using a return value from foo) implies that passing 
one argument to a function is, in the general case, conceptually related to 
assigning a value. And that is an absurdity and abuse of syntax on the same 
level as pretending that string concatenation is an "addition" or that 
outputting is a "<<", etc.

And this "solution" that Tango was forced to use as a result of that kludge 
doesn't totally solve the problem since it leaves symantic gibberish like 
this as perfectly-compileable:

ts.fromSeconds = 30; // WTF is that supposed to mean?!? But it compiles 
anyway! 
Mar 27 2009
next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Fri, 27 Mar 2009 09:09:27 +0100, Nick Sabalausky <a a.a> wrote:

 I don't mean to put words in the Tango crew's mouths, but IMO this piece
 from 0.99.8's changelog is a great demonstration of what's wrong with D's
 sloppy "accessor" syntax:

 -------------
 tango.time

 * TimeSpan.seconds(ulong), etc. is now replaced by
 TimeSpan.fromSeconds(ulong), etc. The original form causes problems when
 people write code like:

 auto ts = TimeSpan.seconds(60);
 ts.seconds = 30; // you would think this would assign 30 seconds to ts,  
 but
 what it does is create a
                  // temporary and throw it away.
 -------------

 The fact that D lets you freely switch between "foo(x)" and "foo = x" (at
 least when you're not using a return value from foo) implies that passing
 one argument to a function is, in the general case, conceptually related  
 to
 assigning a value. And that is an absurdity and abuse of syntax on the  
 same
 level as pretending that string concatenation is an "addition" or that
 outputting is a "<<", etc.

 And this "solution" that Tango was forced to use as a result of that  
 kludge
 doesn't totally solve the problem since it leaves symantic gibberish like
 this as perfectly-compileable:

 ts.fromSeconds = 30; // WTF is that supposed to mean?!? But it compiles
 anyway!

I feel the problem here is that you can access a type's static members through an instance of it, not so much the property syntax. One could argue that auto foo = TimeSpan.seconds = 30; looks weird, but writing weird code is in no way dependent on property syntax. -- Simen
Mar 27 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"Simen Kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.urfzx0ia1hx7vj biotronic-pc.osir.hihm.no...
 I feel the problem here is that you can access a type's static members
 through an instance of it, not so much the property syntax.

That didn't even occur to me. That's certainly a problem too.
Mar 27 2009
prev sibling next sibling parent "Nick Sabalausky" <a a.a> writes:
"Simen Kjaeraas" <simen.kjaras gmail.com> wrote in message 
news:op.urfzx0ia1hx7vj biotronic-pc.osir.hihm.no...
 One could argue that auto foo = TimeSpan.seconds = 30; looks weird, but
 writing weird code is in no way dependent on property syntax.

True, but a function's/class's/data-member's interface shouldn't help facilitate weird code. The creator of the function/class/data-member knows the semantics of whatever interface they're creating, so *they* are the proper ones who should have to choose between function syntax and property syntax, *not* the user of the interface.
Mar 27 2009
prev sibling parent Michel Fortin <michel.fortin michelf.com> writes:
On 2009-03-27 04:45:26 -0400, "Simen Kjaeraas" <simen.kjaras gmail.com> said:

 I feel the problem here is that you can access a type's static members
 through an instance of it, not so much the property syntax.
 
 One could argue that auto foo = TimeSpan.seconds = 30; looks weird, but
 writing weird code is in no way dependent on property syntax.

Perhaps static being accessible from the instance is a problem, but you can easily work around the problem by making the function's name clearer by adding a verb. If you had: auto foo = TimeSpan.createFromSeconds = 30; it'd still look strange, sure, but the intent would still be pretty clear. So in short functions that perform an action should have a verb. -- Michel Fortin michel.fortin michelf.com http://michelf.com/
Mar 27 2009
prev sibling next sibling parent reply BCS <none anon.com> writes:
Hello Nick,

 I don't mean to put words in the Tango crew's mouths, but IMO this
 piece from 0.99.8's changelog is a great demonstration of what's wrong
 with D's sloppy "accessor" syntax:
 
 -------------
 tango.time
 * TimeSpan.seconds(ulong), etc. is now replaced by
 TimeSpan.fromSeconds(ulong), etc. The original form causes problems
 when people write code like:
 
 auto ts = TimeSpan.seconds(60);
 ts.seconds = 30; // you would think this would assign 30 seconds to
 ts, but
 what it does is create a
 // temporary and throw it away.
 -------------
 The fact that D lets you freely switch between "foo(x)" and "foo = x"
 (at least when you're not using a return value from foo) implies that
 passing one argument to a function is, in the general case,
 conceptually related to assigning a value. And that is an absurdity
 and abuse of syntax on the same level as pretending that string
 concatenation is an "addition" or that outputting is a "<<", etc.
 
 And this "solution" that Tango was forced to use as a result of that
 kludge doesn't totally solve the problem since it leaves symantic
 gibberish like this as perfectly-compileable:
 
 ts.fromSeconds = 30; // WTF is that supposed to mean?!? But it
 compiles anyway!
 

One option would be to only allow that fn = v syntax where fn returns void.
Mar 27 2009
next sibling parent "Nick Sabalausky" <a a.a> writes:
"BCS" <none anon.com> wrote in message 
news:a6268ff401a8cb7cd0afe22784 news.digitalmars.com...
 ts.fromSeconds = 30; // WTF is that supposed to mean?!? But it
 compiles anyway!

One option would be to only allow that fn = v syntax where fn returns void.

That still doesn't plug all the holes. Just because a function takes one argument and returns void still doesn't necessarily imply that it does something that could be reasonably considered "setting". --------- Stdout.formatln = "Hello"; // Still doesn't make much sense --------- class Foo { // Complex set of private data members here void MutateFooInPlace(bool optionA) { // Do some sort of fancy in-place mutation of Foo // "optionA" is some sort of algorithm-adjusting option. } } auto f = new Foo(); f. MutateFooInPlace = false; // Even worse! ---------
Mar 27 2009
prev sibling parent reply Derek Parnell <derek psych.ward> writes:
On Fri, 27 Mar 2009 18:57:50 +0300, Denis Koroskin wrote:

 This way you can't do "a = b = c = 42;"-style chaining.

A bit off topic, but what's so good about that coding style anyway? I still think that readibilty and maintability is enhanced by coding ... // Set all to same value a = 42; b = 42; c = 42; or even ... // Set all to c's value c = 42; b = c; a = c; -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Mar 27 2009
parent "Nick Sabalausky" <a a.a> writes:
"Derek Parnell" <derek psych.ward> wrote in message 
news:b3ttq8dh6euj.6tg6gfg6kfnj.dlg 40tude.net...
 On Fri, 27 Mar 2009 18:57:50 +0300, Denis Koroskin wrote:

 This way you can't do "a = b = c = 42;"-style chaining.

A bit off topic, but what's so good about that coding style anyway?

I've been known to use it now and then if there's a few variables I'm initing to the same value (assuming of course that the whole point is for the variables to start out at the same value). It's certainly not a huge improvement, but I see nothing wrong with it.
Mar 27 2009
prev sibling next sibling parent reply "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 27 Mar 2009 18:53:51 +0300, BCS <none anon.com> wrote:

 Hello Nick,

 I don't mean to put words in the Tango crew's mouths, but IMO this
 piece from 0.99.8's changelog is a great demonstration of what's wrong
 with D's sloppy "accessor" syntax:

 -------------
 tango.time
 * TimeSpan.seconds(ulong), etc. is now replaced by
 TimeSpan.fromSeconds(ulong), etc. The original form causes problems
 when people write code like:

 auto ts = TimeSpan.seconds(60);
 ts.seconds = 30; // you would think this would assign 30 seconds to
 ts, but
 what it does is create a
 // temporary and throw it away.
 -------------
 The fact that D lets you freely switch between "foo(x)" and "foo = x"
 (at least when you're not using a return value from foo) implies that
 passing one argument to a function is, in the general case,
 conceptually related to assigning a value. And that is an absurdity
 and abuse of syntax on the same level as pretending that string
 concatenation is an "addition" or that outputting is a "<<", etc.

 And this "solution" that Tango was forced to use as a result of that
 kludge doesn't totally solve the problem since it leaves symantic
 gibberish like this as perfectly-compileable:

 ts.fromSeconds = 30; // WTF is that supposed to mean?!? But it
 compiles anyway!

One option would be to only allow that fn = v syntax where fn returns void.

Obscure rule and a bad idea in general. This way you can't do "a = b = c = 42;"-style chaining.
Mar 27 2009
parent BCS <none anon.com> writes:
Hello Denis,

 On Fri, 27 Mar 2009 18:53:51 +0300, BCS <none anon.com> wrote:
 
 One option would be to only allow that fn = v syntax where fn returns
 void.
 

c = 42;"-style chaining.

I'd say that chaining example would be a bad idea anyway. strange things would start happening if b or c altered the value or returned a different type. One option, if you insist on chaining working, would be to restrict it to void returns and have the expression evaluate to the RHS.
Mar 27 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 27 Mar 2009 13:44:48 -0400, Nick Sabalausky <a a.a> wrote:

 "Simen Kjaeraas" <simen.kjaras gmail.com> wrote in message
 news:op.urfzx0ia1hx7vj biotronic-pc.osir.hihm.no...
 I feel the problem here is that you can access a type's static members
 through an instance of it, not so much the property syntax.

That didn't even occur to me. That's certainly a problem too.

Yes, that was my main beef with the compiler when I had to fix it. I'd prefer static member functions not be callable on instances. On the other hand, accessing static members is sometimes convenient: ReallyReallyLongStructName n; n = n.init; Note that even with the poposed fix, this is valid code which makes for much confusion: TimeSpan.fromSeconds = 30; Which essentially does nothing, but looks like it's setting a static property. I agree with you that the designer of a class should be the one to define its interface, not the user. I've always wished for the ability to specify which functions should be properties and which ones should not. BTW, I think the way C# properties work with chaining is to do this: // a b c are properties a = b = c = 42; translates to: set_c(42); set_b(get_c()); set_a(get_b()); -Steve
Mar 27 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 27 Mar 2009 21:56:36 +0300, Steven Schveighoffer <schveiguy yahoo.com>
wrote:

 On Fri, 27 Mar 2009 13:44:48 -0400, Nick Sabalausky <a a.a> wrote:

 "Simen Kjaeraas" <simen.kjaras gmail.com> wrote in message
 news:op.urfzx0ia1hx7vj biotronic-pc.osir.hihm.no...
 I feel the problem here is that you can access a type's static members
 through an instance of it, not so much the property syntax.

That didn't even occur to me. That's certainly a problem too.

Yes, that was my main beef with the compiler when I had to fix it. I'd prefer static member functions not be callable on instances. On the other hand, accessing static members is sometimes convenient: ReallyReallyLongStructName n; n = n.init;

int x = 5; writefln(x.init); // prints 0; how is that possible? It should certainly be either disallowed or yield 5. int x = 5; writefln(typeof(x).init); // 0 as expected Now this one is fine.
 Note that even with the poposed fix, this is valid code which makes for
 much confusion:

 TimeSpan.fromSeconds = 30;

 Which essentially does nothing, but looks like it's setting a static
 property.

 I agree with you that the designer of a class should be the one to define
 its interface, not the user.  I've always wished for the ability to
 specify which functions should be properties and which ones should not.

 BTW, I think the way C# properties work with chaining is to do this:

 // a b c are properties
 a = b = c = 42;

 translates to:
 set_c(42);
 set_b(get_c());
 set_a(get_b());

 -Steve
 

Mar 27 2009
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 27 Mar 2009 15:13:16 -0400, Denis Koroskin <2korden gmail.com>  
wrote:

 On Fri, 27 Mar 2009 21:56:36 +0300, Steven Schveighoffer  
 <schveiguy yahoo.com> wrote:

 On Fri, 27 Mar 2009 13:44:48 -0400, Nick Sabalausky <a a.a> wrote:

 "Simen Kjaeraas" <simen.kjaras gmail.com> wrote in message
 news:op.urfzx0ia1hx7vj biotronic-pc.osir.hihm.no...
 I feel the problem here is that you can access a type's static members
 through an instance of it, not so much the property syntax.

That didn't even occur to me. That's certainly a problem too.

Yes, that was my main beef with the compiler when I had to fix it. I'd prefer static member functions not be callable on instances. On the other hand, accessing static members is sometimes convenient: ReallyReallyLongStructName n; n = n.init;

int x = 5; writefln(x.init); // prints 0; how is that possible?

Because init is a static member of the type. x.init is equivalent to int.init. If you want a different default, I think you do something like: typedef int myint = 5; myint x; writefln(x.init); // prints 5
 It should certainly be either disallowed or yield 5.

 int x = 5;
 writefln(typeof(x).init); // 0 as expected

 Now this one is fine.

yeah, that works, but I don't like it as much. I suppose it wouldn't be horrible to use typeof(x) everywhere you need static data, but it does make things much nicer, especially when you have things like enums or constants to just access them. -Steve
Mar 27 2009
prev sibling next sibling parent Jarrett Billingsley <jarrett.billingsley gmail.com> writes:
On Fri, Mar 27, 2009 at 3:13 PM, Denis Koroskin <2korden gmail.com> wrote:
 int x = 5;
 writefln(x.init); // prints 0; how is that possible?

 It should certainly be either disallowed or yield 5.

In DMD pre-1.017, it would print 5. The language spec was changed with that version.
Mar 27 2009
prev sibling next sibling parent "Denis Koroskin" <2korden gmail.com> writes:
On Fri, 27 Mar 2009 22:37:39 +0300, Jarrett Billingsley
<jarrett.billingsley gmail.com> wrote:

 On Fri, Mar 27, 2009 at 3:13 PM, Denis Koroskin <2korden gmail.com> wrote:
 int x = 5;
 writefln(x.init); // prints 0; how is that possible?

 It should certainly be either disallowed or yield 5.

In DMD pre-1.017, it would print 5. The language spec was changed with that version.

Yeah, I know, but still uneasy with the spec change.
Mar 27 2009
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Nick Sabalausky wrote:
 I don't mean to put words in the Tango crew's mouths, but IMO this piece 
 from 0.99.8's changelog is a great demonstration of what's wrong with D's 
 sloppy "accessor" syntax:

I agree, it's a hack. I really enjoy using properties, too, but I think C# has the right idea for them.
Mar 27 2009