www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - property

reply "Adam D. Ruppe" <destructionator gmail.com> writes:
So the existing -property in the compiler as absolutely 
worthless. It is semantically wrong.

 property void function() func() {
         return { assert(0, "success"); };
}

void main() {
         func(); // should assert 0 success, doesn't
	auto dele = &func;
	dele(); // should assert 0 success, doesn't
}

Does the same thing with and without -property, and it is
wrong both ways.


Now, let's talk about doing it right. First, I think the entire
getter design in the compiler is counter productive. It looks
like it is part of the AssignExp... but you want getters to work
in a lot of places, not just AssignExp.



It looks to me that it actually belongs in the resolveProperties 
function in expression.c, or maybe even somewhere else since I 
tried this and it didn't quite work. I think this is operating on 
a whole line, not just the individual pieces. But regardless, it 
definitely belongs on a lower level than it is.



See, if you ask me, every single mention of a function name that 
is marked  property needs to be rewritten. This might break 
setters, but we'll cross that bridge when we come to it.


When we look back at the test code:

         func(); // should assert 0 success, doesn't
	auto dele = &func;


These two lines both currently refer to the getter function
itself. We want the getter function to be completely invisible
to all other code.


This should be rewritten kinda like if this were in the code:
   #define func (func())


This would also fix things like

func += 10;

if func returned a ref int.

===
 property ref int func() {
         static int a;
         return a;
}

void main() {
         // all of these should work; func should look just like 
an int to the outside world
         int a = func;
         int* b = &func;
         func += 10;
}
===
callexp.d(19): Error: cannot implicitly convert expression (& 
func) of type int function()  property ref to int*




Let's try running our "macro" over it

void main() {
         int a = (func());
         int* b = &(func());
         (func()) += 10;
}


What ACTUALLY happens right now is that works. Indeed, you HAVE 
to use the () right now to make it work!


What SHOULD happen:

callexp.d(18): Error: function expected before (), not func() of 
type int
callexp.d(20): Error: function expected before (), not func() of 
type int

Or something like that. Since  property ref int func() is 
supposed to be interchangable for a plain int func;, no special 
enforcement of  property syntax is required: it is illegal to 
call an int like a function, and func is an int as far as the 
outside world is concerned.


This is how the property syntax is enforced: by the compiler's 
existing type checks. No additional code should be there to check 
syntax.





So, I have two questions:

1) Do we all agree that  properties should be interchangeable for 
plain variables of the return value's type in all contexts? That 
is, if I can make this work with a pull request, is that going to 
be acceptable?

2) Does anyone who knows the compiler better want to give me some 
hints on the implementation? I'm still pretty sure the 
resolveProperties function is too far into it, but func on its 
own is just a void* to the function I believe at this point so... 
yeah that is complicating things. Perhaps *that* is what needs to 
change.


I'm also open to ideas on setters, but right now getter is what I 
want to focus on. If we have to rewrite a getter call on the left 
hand side of an assign exp into a setter call, we can figure that 
out later. Hell, it might even just work.  But one thing at a 
time.
Aug 04 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
I should add: if a function is not marked  property, it should 
not change at all: it should be callable with and without parens, 
including sometimes referring to the wrong thing, exactly like it 
is now.

I come to fix code, not to break it. Rewriting references to 
 property function names should fix all property related issues 
without breaking any existing code (except that which explicitly 
specified  property but depended on the wrong semantics, code 
which is arguably already broken, just like any other bug fix can 
do.)
Aug 04 2012
parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 I should add: if a function is not marked  property, it should 
 not change at all: it should be callable with and without 
 parens,
I think I'd like functions not marked with property to require the (). Bye, bearophile
Aug 04 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 5 August 2012 at 02:17:18 UTC, bearophile wrote:
 I think I'd like functions not marked with  property to require 
 the ().
You have the -property switch for breaking piles of perfectly good code while enabling no new functionality. What I'm doing is completely independent of -property: I'm making properties actually work. And I'm almost there... I just got the struct members working but introduced a regression where dmd assert fails.
Aug 04 2012
next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, August 05, 2012 04:25:26 Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 02:17:18 UTC, bearophile wrote:
 I think I'd like functions not marked with  property to require
 the ().
You have the -property switch for breaking piles of perfectly good code while enabling no new functionality. What I'm doing is completely independent of -property: I'm making properties actually work. And I'm almost there... I just got the struct members working but introduced a regression where dmd assert fails.
And I think that the only thing that the -property switch currently gets _right_ is complaining about non-property functions getting called with parens. It's very buggy. Regardless, there are indeed two general issues here: 1. Make property work in general. 2. Make -property work correctly. - Jonathan M Davis
Aug 04 2012
prev sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 You have the -property switch for breaking piles of perfectly 
 good code while enabling no new functionality.
The -property switch is present since lot of time, and I think most people that write D2 code compile their libraries with -property. So I don't think it breaks lot of D2 code today.
 What I'm doing is completely independent of -property: I'm 
 making  properties actually work.
I understand. Don't rush things, even if your design takes few days we are dealing with those problems since years, so few more days are not a problem. Bye, bearophile
Aug 04 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, August 05, 2012 05:59:34 bearophile wrote:
 Adam D. Ruppe:
 You have the -property switch for breaking piles of perfectly
 good code while enabling no new functionality.
The -property switch is present since lot of time, and I think most people that write D2 code compile their libraries with -property. So I don't think it breaks lot of D2 code today.
I'd be very surprised if all that many people compile with -property. I'm pretty sure that there are a lot of people (the majority?) who don't even compile with -w or -wi. Obviously, some people do, but from what I've seen, I'd guess that most people compile with the bare minimum flags required, and - property is _very_ buggy anyway. - Jonathan M Davis
Aug 04 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with 
 -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. Another thing I do is I have a template function called getDocument in a lot of my code. It takes the document name as a template param, because in a lot of cases, I want to build the doc at compile time. auto document = getDocument!"about-me"; Is that a function or a property? I think it is a function, it does a lot work. But, the arguments are there already... do we really need to say getDocument!"about-me"() just to appease a compiler switch? (This function also can take some runtime params, but it has defaults so param less calling works as well.) What about std.conv? This works fine in all old code - its functions either naturally require parens (such as to!(int)("10"), gotta have at least the outer set, or octal!"555" - it is an enum, so gotta not use them).... but with UFCS, that's actually out the window now: int i = "10".to!int; prophate.d(4): Error: not a property "10".to!(int) Now, I've never actually written this. With map!(), I do that in the real world, but not with this. Still, I could imagine someone who might. If we had the strictness before 2.059, I could live with it. Just add the () (though, then queue the people complaining about syntatic noise!). But, now we're several months past that. I say that ship has sailed. The big breakage for me is something I have been doing for years though, and that's using my dom.d with chaining: import arsd.dom; // in one place I might use: auto ele = Element.make("span", "Hello").className("hello").setAttribute("foo", "bar"); // but in another I say: ele.className = "something"; Now, many of my newer functions obviate this kind of thing; Element.make now offers overloads for inner text, inner html, and common attributes. There's now an addClass() so it isn't all dependent on className. But, there's still hundreds of lines of me using the older functions like this, and every so often, it still comes up. What happens with -property? prophate.d(7): Error: not a property ele.className arsd/domconvenience.d(49): Error: not a property className arsd/domconvenience.d(81): Error: not a property n.strip arsd/domconvenience.d(81): Error: not a property className arsd/domconvenience.d(88): Error: not a property className arsd/domconvenience.d(218): Error: not a property e.innerHTML arsd/dom.d(201): Error: not a property e.innerText arsd/dom.d(212): Error: not a property e.innerText arsd/dom.d(217): Error: not a property e.innerText arsd/dom.d(232): Error: not a property e.innerText arsd/dom.d(234): Error: not a property e.className arsd/dom.d(242): Error: not a property m.innerHTML arsd/dom.d(506): Error: not a property name.toLower arsd/dom.d(508): Error: not a property value.strip arsd/dom.d(1162): Error: not a property child.innerText arsd/dom.d(1634): Error: not a property innerText arsd/dom.d(1831): Error: not a property e.innerText arsd/dom.d(1859): Error: not a property child.innerText arsd/dom.d(1913): Error: not a property e.innerText arsd/dom.d(2135): Error: not a property this.captionElement().innerText arsd/dom.d(2140): Error: not a property this.captionElement().innerText wooo, but only the first line is this specific example. You can also see in here strip() and toLower() on which is just me being lax: auto v = value.strip.toLower(); for example. Adding the inner parens would be annoying but it wouldn't require changing the form of the code like changing the dual use ones. ....huh, my example here actually does work if I set property, but still use it as a function. Buggy switch. But I know I've tried this before and had one set of usages break with property and another break without it. Maybe it was innerHTML, which can also be overloaded to take an Appender argument. Yeah, then I get this: arsd/dom.d(982): Error: cannot overload both property and non-property functions There's just no winning without redoing a LOT of code, changing names, changing some expressions into statements since the chaining is out, duplicating functionality across two functions when one could do... It was at that point that I decided it'd be better to put my effort into killing -property than trying to "fix" my code to appease its idiotic demands. And now the UFCS map, filter, etc. chains and whatnot are just icing on the cake. Bearophile, I've seen you complain about the mess of parenthesis in std.algorithm before. With ufcs and template arguments, you can be rid of many of them. It is actually pretty beautiful, even to me. Do you really want to break that now? My position right now is property has a strict syntax out of necessity. Stuff without property should work the way it does now - thus minimizing broken code to that which already opted-in to property (which generally does it right anyway), while keeping the status quo on the rest. It can turn out ugly, I'll agree, but it can be beautiful too and just plain isn't worth the code breakage either way.
Aug 05 2012
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 05-Aug-12 18:32, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code.
+1 [snip]
 And now the UFCS map, filter, etc. chains and whatnot are just icing on
 the cake. Bearophile, I've seen you complain about the mess of
 parenthesis in std.algorithm before. With ufcs and template arguments,
 you can be rid of many of them. It is actually pretty beautiful, even to
 me.

 Do you really want to break that now?


 My position right now is  property has a strict syntax out of necessity.
 Stuff without  property should work the way it does now - thus
 minimizing broken code to that which already opted-in to  property
 (which generally does it right anyway), while keeping the status quo on
 the rest. It can turn out ugly, I'll agree, but it can be beautiful too
 and just plain isn't worth the code breakage either way.
Well in the end I think strict property enforcement was meant to solve some corner cases (e.g. returning delegates from, and taking address of no-arg functions). But then it promptly killed at least one cool idiom (chaining & assignment in one name). Then once UFCS was finally working property spoiled it (e.g. your algorithm examples). So I'm with you - let non- property work as before, and property ones to be god damn properly enforced. -- Dmitry Olshansky
Aug 05 2012
prev sibling next sibling parent "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On Sun, 05 Aug 2012 16:32:49 +0200, Adam D. Ruppe  
<destructionator gmail.com> wrote:

 import std.algorithm;
 foreach(i; [1,2,3].map!"a+1") {

 }
 prophate.d(5): Error: not a property [1,2,3].map!("a+1")


 Of course, this is relatively new, using ufcs in 2.059, so the breakage  
 probably isn't too bad, but I'm not the only one who writes it this way  
 - I've seen a number of reddit and newsgroup comments do this too,  
 especially when chaining it.
Yeah. I seem to do that every single time I want to give an example. -- Simen
Aug 05 2012
prev sibling next sibling parent reply "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 octal!"555" - it is an enum, so gotta not use them).... but 
 with UFCS, that's actually out the window now:
What about turning octal into a propety to fix this problem?
 There's just no winning without redoing a LOT of code, changing 
 names, changing some expressions into statements since the 
 chaining is out, duplicating functionality across two functions 
 when one could do...
All my large amount of D2 code gives no warning with -property. Maybe you should fix your code. Bye, bearophile
Aug 05 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 5 August 2012 at 15:45:37 UTC, bearophile wrote:
 What about turning octal into a propety to fix this problem?
octal actually works because it isn't a function. But in general, changing non-properties to properties just for syntax is backward anyway. Whether something is a property or not isn't a question of parenthesis. I think it was a mistake to conflate "property" with "parenthesis-less syntax" in the first place, since they aren't really the same thing. A getX/setX pair in C++ is conceptually a property, but uses a different syntax. A method call in Ruby is still a method call, despite being able to write it without parens. Something should be marked property because it fits the conceptual definition, not because you want to leave parens out. That's also why I don't like -property's implementation: it focuses purely on the syntax, without worrying about the concept. If you get the concept right, the syntax will fit on its own - for example, a property returning an int shouldn't be callable with () because calling an int like a function is nonsense regardless.
 All my large amount of D2 code gives no warning with -property. 
 Maybe you should fix your code.
Maybe you should pay my mortgage.
Aug 05 2012
parent "bearophile" <bearophileHUGS lycos.com> writes:
Adam D. Ruppe:

 Maybe you should pay my mortgage.
I understand. The kind of programs I write enjoy run-time errors raised on overflow on integral values operations. Recently I opened a silly thread about this in D learn. Adding those to D will help me pay my bills and better keep my mind. Bye, bearophile
Aug 05 2012
prev sibling parent reply "Kapps" <opantm2+spam gmail.com> writes:
On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis 
 wrote:
 I'd be very surprised if all that many people compile with 
 -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a () anyways? Seems like it defeats the purpose. To me, when comparing range.filter!"a > 2".map!"a*a".countUntil(3) to range.filter!"a > 2"().map!"a*a"().countUntil(3) Those extra paranthesis just don't do anything, they don't give extra meaning, they don't accomplish anything useful but distract from the actual expression. Most importantly though, with the focus on avoiding breaking code, why are we putting in -property which has the *sole* purpose of breaking existing code. And a whole lot of it at that. Never mind that a huge number of people strongly dislike the idea of enforcing paranthesis in the first place. It's not even about whether I agree with enforcing it, it's that I strongly disagree with breaking code for no benefit besides what some people will arbitrarily think is cleaner code, and others will not.
Aug 05 2012
next sibling parent reply =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Sun, Aug 5, 2012 at 2:32 PM, Kapps <opantm2+spam gmail.com> wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a () anyways? Seems like it defeats the purpose. To me, when comparing range.filter!"a > 2".map!"a*a".countUntil(3) to range.filter!"a > 2"().map!"a*a"().countUntil(3) Those extra paranthesis just don't do anything, they don't give extra meaning, they don't accomplish anything useful but distract from the actual expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented. I wonder if the module signatures would look different if you designed it to use the current features of D. To be honest, when first saw 'some_array.map!"a+1"()' a few years back when I first learned about D my knee jerk reaction was: "from where in the world is 'a' coming from?". I remember I had to read std/algorithm.d and whatever module implements unary "string" functions to figure out what was going on. Anyways, I digress. Part of me looks forward to the day that I can use D reliably another part of my wants to just wait for Rust to hopefully get it "right". Thanks, -Jose
Aug 06 2012
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/07/2012 07:59 AM, José Armando García Sancio wrote:
 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>  wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a () anyways? Seems like it defeats the purpose. To me, when comparing range.filter!"a> 2".map!"a*a".countUntil(3) to range.filter!"a> 2"().map!"a*a"().countUntil(3) Those extra paranthesis just don't do anything, they don't give extra meaning, they don't accomplish anything useful but distract from the actual expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented.
String lambdas would quite certainly have been left out. Anyway, string lambdas are unrelated to the discussion of std.algorithm idioms in this context. [1,2,3].map!(a=>a+1) [1,2,3].map!(a=>a+1)()
Aug 07 2012
parent reply =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Tue, Aug 7, 2012 at 10:39 AM, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 08/07/2012 07:59 AM, Jos=E9 Armando Garc=EDa Sancio wrote:
 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>  wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the sa=
me
 results: it doesn't solve problems and complains about code.

 Some examples of things that break:

 import std.algorithm;
 foreach(i; [1,2,3].map!"a+1") {

 }
 prophate.d(5): Error: not a property [1,2,3].map!("a+1")


 Of course, this is relatively new, using ufcs in 2.059, so the breakag=
e
 probably isn't too bad, but I'm not the only one who writes it this wa=
y
 -
 I've seen a number of reddit and newsgroup comments do this too,
 especially
 when chaining it.
 [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a =
()
 anyways? Seems like it defeats the purpose. To me, when comparing
 range.filter!"a>  2".map!"a*a".countUntil(3)
 to
 range.filter!"a>  2"().map!"a*a"().countUntil(3)
 Those extra paranthesis just don't do anything, they don't give extra
 meaning, they don't accomplish anything useful but distract from the
 actual
 expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented.
String lambdas would quite certainly have been left out. Anyway, string lambdas are unrelated to the discussion of std.algorithm idioms in this context. [1,2,3].map!(a=3D>a+1) [1,2,3].map!(a=3D>a+1)()
Yep. The "problem" is not string lambda but that the mapping function is passed a template argument (which you need for string lambda to work). I guess that this is nice because you can alias the template or pass multiple lambdas (a little obscure but okay). If the lambda function was passed as a runtime parameter and not a template parameter can just do: [1, 2, 3].map(a=3D>a+1) If this was the case we wouldn't be talking about this non-property vs property with regard to map. I other words the map signature could look as follow (pseudo-code): Range!T map(T, U)(Range!U range, T delegate(U)); Thanks, -Jose P.S. You can still alias the map using a closure that binds the delegate but I digress.
Aug 07 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-08-07 20:37, José Armando García Sancio wrote:

 Yep. The "problem" is not string lambda but that the mapping function
 is passed a template argument (which you need for string lambda to
 work). I guess that this is nice because you can alias the template or
 pass multiple lambdas (a little obscure but okay). If the lambda
 function was passed as a runtime parameter and not a template
 parameter can just do:

 [1, 2, 3].map(a=>a+1)
I'm not entirely sure but I think using an alias-parameter the delegate will be inlined. Delegates are in general not inlined. -- /Jacob Carlborg
Aug 07 2012
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 08/07/2012 08:37 PM, José Armando García Sancio wrote:
 On Tue, Aug 7, 2012 at 10:39 AM, Timon Gehr<timon.gehr gmx.ch>  wrote:
 On 08/07/2012 07:59 AM, José Armando García Sancio wrote:
 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>   wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a () anyways? Seems like it defeats the purpose. To me, when comparing range.filter!"a> 2".map!"a*a".countUntil(3) to range.filter!"a> 2"().map!"a*a"().countUntil(3) Those extra paranthesis just don't do anything, they don't give extra meaning, they don't accomplish anything useful but distract from the actual expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented.
String lambdas would quite certainly have been left out. Anyway, string lambdas are unrelated to the discussion of std.algorithm idioms in this context. [1,2,3].map!(a=>a+1) [1,2,3].map!(a=>a+1)()
Yep. The "problem" is not string lambda but that the mapping function is passed a template argument (which you need for string lambda to work).
It is also a matter of execution speed and GC pressure. None of the D compilers is able to inline delegate parameters at least as well as alias template parameters afaik. Furthermore, only the current form can _guarantee_ that no hidden GC allocations take place due to parameter passing to map.
 I guess that this is nice because you can alias the template or
 pass multiple lambdas (a little obscure but okay). If the lambda
 function was passed as a runtime parameter and not a template
 parameter can just do:

 [1, 2, 3].map(a=>a+1)
Well, no you cannot, because the current language would not be able to deduce a type for 'a'.
 If this was the case we wouldn't be talking about this non-property vs
 property with regard to map.
We would be talking about improving IFTI to support cross-parameter type deduction. (and maybe we would constantly be complaining about poor GC performance.) Some programmers who use it now would presumably avoid std.algorithm because of the poor predictability of performance.
Aug 07 2012
next sibling parent reply =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Tue, Aug 7, 2012 at 12:27 PM, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 08/07/2012 08:37 PM, Jos=E9 Armando Garc=EDa Sancio wrote:
 On Tue, Aug 7, 2012 at 10:39 AM, Timon Gehr<timon.gehr gmx.ch>  wrote:
 On 08/07/2012 07:59 AM, Jos=E9 Armando Garc=EDa Sancio wrote:

 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>   wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -propert=
y.
 Indeed. Sometimes I try it just to see what happens, and always the
 same
 results: it doesn't solve problems and complains about code.

 Some examples of things that break:

 import std.algorithm;
 foreach(i; [1,2,3].map!"a+1") {

 }
 prophate.d(5): Error: not a property [1,2,3].map!("a+1")


 Of course, this is relatively new, using ufcs in 2.059, so the
 breakage
 probably isn't too bad, but I'm not the only one who writes it this
 way
 -
 I've seen a number of reddit and newsgroup comments do this too,
 especially
 when chaining it.
 [snip]
I completely agree, particularl with the UFCS part. UFCS is designed =
to
 get
 rid of the horrible mess of (), and now we want to arbitrarily force =
a
 ()
 anyways? Seems like it defeats the purpose. To me, when comparing
 range.filter!"a>   2".map!"a*a".countUntil(3)
 to
 range.filter!"a>   2"().map!"a*a"().countUntil(3)
 Those extra paranthesis just don't do anything, they don't give extra
 meaning, they don't accomplish anything useful but distract from the
 actual
 expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented.
String lambdas would quite certainly have been left out. Anyway, string lambdas are unrelated to the discussion of std.algorithm idioms in this context. [1,2,3].map!(a=3D>a+1) [1,2,3].map!(a=3D>a+1)()
Yep. The "problem" is not string lambda but that the mapping function is passed a template argument (which you need for string lambda to work).
It is also a matter of execution speed and GC pressure. None of the D compilers is able to inline delegate parameters at least as well as alias template parameters afaik. Furthermore, only the current form can _guarantee_ that no hidden GC allocations take place due to parameter passing to map.
Interesting. I can't speak for the GC and allocation you mention since I never tried to implement this myself but I should point out that C++ STL's algorithm "module" (transform, for_each, etc) works this way. Having said that, if D can't infer the type of lambda parameters (which I thought the current version of D could) and create a function/delegate based on the body then this is a no go. Thanks for you insight. -Jose
Aug 07 2012
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 08/07/2012 10:52 PM, José Armando García Sancio wrote:
 On Tue, Aug 7, 2012 at 12:27 PM, Timon Gehr<timon.gehr gmx.ch>  wrote:
 On 08/07/2012 08:37 PM, José Armando García Sancio wrote:
 On Tue, Aug 7, 2012 at 10:39 AM, Timon Gehr<timon.gehr gmx.ch>   wrote:
 On 08/07/2012 07:59 AM, José Armando García Sancio wrote:

 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>    wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the same results: it doesn't solve problems and complains about code. Some examples of things that break: import std.algorithm; foreach(i; [1,2,3].map!"a+1") { } prophate.d(5): Error: not a property [1,2,3].map!("a+1") Of course, this is relatively new, using ufcs in 2.059, so the breakage probably isn't too bad, but I'm not the only one who writes it this way - I've seen a number of reddit and newsgroup comments do this too, especially when chaining it. [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to get rid of the horrible mess of (), and now we want to arbitrarily force a () anyways? Seems like it defeats the purpose. To me, when comparing range.filter!"a> 2".map!"a*a".countUntil(3) to range.filter!"a> 2"().map!"a*a"().countUntil(3) Those extra paranthesis just don't do anything, they don't give extra meaning, they don't accomplish anything useful but distract from the actual expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented.
String lambdas would quite certainly have been left out. Anyway, string lambdas are unrelated to the discussion of std.algorithm idioms in this context. [1,2,3].map!(a=>a+1) [1,2,3].map!(a=>a+1)()
Yep. The "problem" is not string lambda but that the mapping function is passed a template argument (which you need for string lambda to work).
It is also a matter of execution speed and GC pressure. None of the D compilers is able to inline delegate parameters at least as well as alias template parameters afaik. Furthermore, only the current form can _guarantee_ that no hidden GC allocations take place due to parameter passing to map.
Interesting. I can't speak for the GC and allocation you mention since I never tried to implement this myself but I should point out that C++ STL's algorithm "module" (transform, for_each, etc) works this way. Having said that, if D can't infer the type of lambda parameters (which I thought the current version of D could) and create a function/delegate based on the body then this is a no go. Thanks for you insight. -Jose
The reason it works in C++ for transform, for_each etc. is because those functions are eager. Allocation-free transform with runtime delegate in D: import std.algorithm, std.range; void transform(IR,OR,I,O)(IR input, OR output, scope O delegate(I) dg) if(is(typeof(input.map!dg.copy(output)))) { input.map!dg.copy(output); } void main(){ int[3] result; int b = 1; [1,2,3].transform(result[], (int a)=>a+b); assert(result[] == [2,3,4]); } Note that parameter type deduction is lost for the delegate. I'd like type deduction to work if the signature of 'transform' is changed to: void transform(IR,OR,I=ElementType!IR,O)(IR input, OR output, scope O delegate(I) dg); [1,2,3].transform(result[], a=>a+b); Lazy map with runtime delegate would have to remember the delegate, therefore it could not be a scope delegate.
Aug 07 2012
prev sibling parent =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Tue, Aug 7, 2012 at 1:52 PM, Jos=E9 Armando Garc=EDa Sancio
<jsancio gmail.com> wrote:
 On Tue, Aug 7, 2012 at 12:27 PM, Timon Gehr <timon.gehr gmx.ch> wrote:
 On 08/07/2012 08:37 PM, Jos=E9 Armando Garc=EDa Sancio wrote:
 On Tue, Aug 7, 2012 at 10:39 AM, Timon Gehr<timon.gehr gmx.ch>  wrote:
 On 08/07/2012 07:59 AM, Jos=E9 Armando Garc=EDa Sancio wrote:

 On Sun, Aug 5, 2012 at 2:32 PM, Kapps<opantm2+spam gmail.com>   wrote=
:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
Interesting. I can't speak for the GC and allocation you mention since I never tried to implement this myself but I should point out that C++ STL's algorithm "module" (transform, for_each, etc) works this way. Having said that, if D can't infer the type of lambda parameters (which I thought the current version of D could) and create a function/delegate based on the body then this is a no go. Thanks for you insight. -Jose
Just incase it is not clear. I have zero interest in changing or promoting that we change the signature of the std.algorithm module. This is more of a selfish module design exercise on my part. Thanks, -Jose
Aug 07 2012
prev sibling next sibling parent reply Brad Anderson <eco gnuk.net> writes:
On Mon, Aug 6, 2012 at 11:59 PM, Jos=E9 Armando Garc=EDa Sancio <
jsancio gmail.com> wrote:

 On Sun, Aug 5, 2012 at 2:32 PM, Kapps <opantm2+spam gmail.com> wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property.
Indeed. Sometimes I try it just to see what happens, and always the sa=
me
 results: it doesn't solve problems and complains about code.

 Some examples of things that break:

 import std.algorithm;
 foreach(i; [1,2,3].map!"a+1") {

 }
 prophate.d(5): Error: not a property [1,2,3].map!("a+1")


 Of course, this is relatively new, using ufcs in 2.059, so the breakag=
e
 probably isn't too bad, but I'm not the only one who writes it this wa=
y
 -
 I've seen a number of reddit and newsgroup comments do this too,
especially
 when chaining it.
 [snip]
I completely agree, particularl with the UFCS part. UFCS is designed to
get
 rid of the horrible mess of (), and now we want to arbitrarily force a =
()
 anyways? Seems like it defeats the purpose. To me, when comparing
 range.filter!"a > 2".map!"a*a".countUntil(3)
 to
 range.filter!"a > 2"().map!"a*a"().countUntil(3)
 Those extra paranthesis just don't do anything, they don't give extra
 meaning, they don't accomplish anything useful but distract from the
actual
 expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented. I wonder if the module signatures would look different if you designed it to use the current features of D. To be honest, when first saw 'some_array.map!"a+1"()' a few years back when I first learned about D my knee jerk reaction was: "from where in the world is 'a' coming from?". I remember I had to read std/algorithm.d and whatever module implements unary "string" functions to figure out what was going on. Anyways, I digress. Part of me looks forward to the day that I can use D reliably another part of my wants to just wait for Rust to hopefully get it "right". Thanks, -Jose
There was a pull to switch std.algorithm over to the new lambda syntax (by me). It caused the dmd unit tests to fail though. There is also an issue with intermodule visibility of nested structs that popped up and prevented the every function from being switched over. I should probably recreate the pull request so one of the compiler guys can take a look. BA
Aug 06 2012
parent "Brad Anderson" <eco gnuk.net> writes:
On Tuesday, 7 August 2012 at 06:31:46 UTC, Brad Anderson wrote:
 On Mon, Aug 6, 2012 at 11:59 PM, José Armando García Sancio <
 jsancio gmail.com> wrote:
[snip]
 Personally I am not a huge fan of using strings as a way to 
 pass a
 function into a high-order function. I suspect that this 
 string stuff
 became popular because D didn't have lambda declarations and 
 type
 inference when the module was designed and implemented. I 
 wonder if
 the module signatures would look different if you designed it 
 to use
 the current features of D.

 To be honest, when first saw 'some_array.map!"a+1"()' a few 
 years back
 when I first learned about D my knee jerk reaction was: "from 
 where in
 the world is 'a' coming from?". I remember I had to read
 std/algorithm.d and whatever module implements unary "string"
 functions to figure out what was going on. Anyways, I digress.

 Part of me looks forward to the day that I can use D reliably 
 another
 part of my wants to just wait for Rust to hopefully get it 
 "right".

 Thanks,
 -Jose
There was a pull to switch std.algorithm over to the new lambda syntax (by me). It caused the dmd unit tests to fail though. There is also an issue with intermodule visibility of nested structs that popped up and prevented the every function from being switched over. I should probably recreate the pull request so one of the compiler guys can take a look. BA
https://github.com/D-Programming-Language/phobos/pull/744
Aug 07 2012
prev sibling parent =?ISO-8859-1?Q?Jos=E9_Armando_Garc=EDa_Sancio?= <jsancio gmail.com> writes:
On Mon, Aug 6, 2012 at 11:31 PM, Brad Anderson <eco gnuk.net> wrote:
 On Mon, Aug 6, 2012 at 11:59 PM, Jos=E9 Armando Garc=EDa Sancio
 <jsancio gmail.com> wrote:
 On Sun, Aug 5, 2012 at 2:32 PM, Kapps <opantm2+spam gmail.com> wrote:
 On Sunday, 5 August 2012 at 14:32:50 UTC, Adam D. Ruppe wrote:
 On Sunday, 5 August 2012 at 04:12:23 UTC, Jonathan M Davis wrote:
 I'd be very surprised if all that many people compile with -property=
.
 Indeed. Sometimes I try it just to see what happens, and always the
 same
 results: it doesn't solve problems and complains about code.

 Some examples of things that break:

 import std.algorithm;
 foreach(i; [1,2,3].map!"a+1") {

 }
 prophate.d(5): Error: not a property [1,2,3].map!("a+1")


 Of course, this is relatively new, using ufcs in 2.059, so the breaka=
ge
 probably isn't too bad, but I'm not the only one who writes it this w=
ay
 -
 I've seen a number of reddit and newsgroup comments do this too,
 especially
 when chaining it.
 [snip]
I completely agree, particularl with the UFCS part. UFCS is designed t=
o
 get
 rid of the horrible mess of (), and now we want to arbitrarily force a
 ()
 anyways? Seems like it defeats the purpose. To me, when comparing
 range.filter!"a > 2".map!"a*a".countUntil(3)
 to
 range.filter!"a > 2"().map!"a*a"().countUntil(3)
 Those extra paranthesis just don't do anything, they don't give extra
 meaning, they don't accomplish anything useful but distract from the
 actual
 expression.
Just a small comment. I have been following this thread a little and was somewhat surprise that the argument against enforcing parenthesis on non-properties is that a call like [1,2,3].map!"a+1" would look ugly as [1,2,3].map!"a+1"(). To me that is a issue of the std.algorithm module and not so much of the language. Personally I am not a huge fan of using strings as a way to pass a function into a high-order function. I suspect that this string stuff became popular because D didn't have lambda declarations and type inference when the module was designed and implemented. I wonder if the module signatures would look different if you designed it to use the current features of D. To be honest, when first saw 'some_array.map!"a+1"()' a few years back when I first learned about D my knee jerk reaction was: "from where in the world is 'a' coming from?". I remember I had to read std/algorithm.d and whatever module implements unary "string" functions to figure out what was going on. Anyways, I digress. Part of me looks forward to the day that I can use D reliably another part of my wants to just wait for Rust to hopefully get it "right". Thanks, -Jose
There was a pull to switch std.algorithm over to the new lambda syntax (b=
y
 me).  It caused the dmd unit tests to fail though.  There is also an issu=
e
 with intermodule visibility of nested structs that popped up and prevente=
d
 the every function from being switched over.  I should probably recreate =
the
 pull request so one of the compiler guys can take a look.

 BA
Wow. That would be nice! I would like to see such a change though I assume that such a change would break a lot of code. No?
Aug 06 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Sunday, August 05, 2012 04:17:13 bearophile wrote:
 Adam D. Ruppe:
 I should add: if a function is not marked  property, it should
 not change at all: it should be callable with and without
 parens,
I think I'd like functions not marked with property to require the ().
I wholeheartedly agree, but it's a divisive subject. Some of us feel very strongly that functions not marked with property should require parens, and others feel very strongly that they should be optional. - Jonathan M Davis
Aug 04 2012
prev sibling next sibling parent reply deadalnix <deadalnix gmail.com> writes:
Le 04/08/2012 19:13, Adam D. Ruppe a écrit :
 So the existing -property in the compiler as absolutely worthless. It is
 semantically wrong.

  property void function() func() {
 return { assert(0, "success"); };
 }

 void main() {
 func(); // should assert 0 success, doesn't
 auto dele = &func;
 dele(); // should assert 0 success, doesn't
 }

 Does the same thing with and without -property, and it is
 wrong both ways.
Note that something more is wrong. func(); // Do something auto dele = &func; dele(); // Expected to do the same thing, even if we passed throw & in the middle. The whole thing is f***ed up IMO. (and I've made some work on SDC on the subject, the & is really convoluted to implement).
 When we look back at the test code:

 func(); // should assert 0 success, doesn't
 auto dele = &func;
auto dele = func;
 These two lines both currently refer to the getter function
 itself. We want the getter function to be completely invisible
 to all other code.


 This should be rewritten kinda like if this were in the code:
 #define func (func())


 This would also fix things like

 func += 10;

 if func returned a ref int.

 What SHOULD happen:

 callexp.d(18): Error: function expected before (), not func() of type int
 callexp.d(20): Error: function expected before (), not func() of type int
+1
 Or something like that. Since  property ref int func() is supposed to be
 interchangable for a plain int func;, no special enforcement of
  property syntax is required: it is illegal to call an int like a
 function, and func is an int as far as the outside world is concerned.
+1
 1) Do we all agree that  properties should be interchangeable for plain
 variables of the return value's type in all contexts? That is, if I can
 make this work with a pull request, is that going to be acceptable?
Except for that & thing, 100% agreed.
Aug 04 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 August 2012 at 17:35:03 UTC, deadalnix wrote:
 Except for that & thing, 100% agreed.
I think you're right about that too...
Aug 04 2012
prev sibling next sibling parent "Adam D. Ruppe" <destructionator gmail.com> writes:
I think I've cracked the getters...

I opened expression.c in DMD and surfed to line 2940 or so. Looks 
like this:

         {
             error("forward reference to %s", toChars());
             return new ErrorExp();
         }

         return new VarExp(loc, f, hasOverloads);


The function is



Expression *DsymbolExp::semantic(Scope *sc)


Instead of return new VarExp, I made it:


         VarExp* varexp = new VarExp(loc, f, hasOverloads);

         TypeFunction* tf = (TypeFunction *)f->type;
         if(tf->isproperty) {
             CallExp* ce = new CallExp(loc, varexp);
             ce->semantic(sc);
             return ce;
         } else {
             return varexp;
         }




And now my test functions work... the question is, did I break a 
lot of other stuff? idk, but this is a big move forward.
Aug 04 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-08-04 19:13, Adam D. Ruppe wrote:

 This would also fix things like

 func += 10;

 if func returned a ref int.

 ===
  property ref int func() {
          static int a;
          return a;
 }

 void main() {
          // all of these should work; func should look just like an int
 to the outside world
          int a = func;
          int* b = &func;
          func += 10;
 }
 ===
 callexp.d(19): Error: cannot implicitly convert expression (& func) of
 type int function()  property ref to int*
I don't think that's correct behavior. I think the correct behavior would be to have a property rewrite, something like this: foo += 10; Is rewritten as: auto __tmp = foo; foo = __tmp + 10;
 Or something like that. Since  property ref int func() is supposed to be
 interchangable for a plain int func;, no special enforcement of
  property syntax is required: it is illegal to call an int like a
 function, and func is an int as far as the outside world is concerned.

 This is how the property syntax is enforced: by the compiler's existing
 type checks. No additional code should be there to check syntax.

 So, I have two questions:

 1) Do we all agree that  properties should be interchangeable for plain
 variables of the return value's type in all contexts? That is, if I can
 make this work with a pull request, is that going to be acceptable?
I think that you should always be able to replace a variable with a property. The other way around I'm not so sure. The problem is with methods in classes. Since a method will be virtual by default you can't just replace a property with a variable. That could potentially break subclasses that override the property. -- /Jacob Carlborg
Aug 04 2012
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-08-04 21:08, Jacob Carlborg wrote:

 I think that you should always be able to replace a variable with a
 property. The other way around I'm not so sure. The problem is with
 methods in classes. Since a method will be virtual by default you can't
 just replace a property with a variable. That could potentially break
 subclasses that override the property.
I wouldn't actually mind a way to do this, perhaps something like this: class Foo { property int bar: } Would be the same as: class Foo { private int bar_: property int bar () { return bar_; } property int bar (int value) { return bar_ = value; } } -- /Jacob Carlborg
Aug 04 2012
parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, August 04, 2012 21:11:47 Jacob Carlborg wrote:
 On 2012-08-04 21:08, Jacob Carlborg wrote:
 I think that you should always be able to replace a variable with a
 property. The other way around I'm not so sure. The problem is with
 methods in classes. Since a method will be virtual by default you can't
 just replace a property with a variable. That could potentially break
 subclasses that override the property.
I wouldn't actually mind a way to do this, perhaps something like this: class Foo { property int bar: } Would be the same as: class Foo { private int bar_: property int bar () { return bar_; } property int bar (int value) { return bar_ = value; } }1
That would be kind of cool, though I would have suggested that putting property on a variable would make it so that you couldn't do anything with it that you couldn't do with a property (e.g. taking the address of a variable will break when it's switched to a property, and property on the variable could prevent that). But maybe your proposal is better - though I'm not sure how much I'd end up using it, because if you wanted to actually use the member variable, you'd get into naming issues. You proposed bar_ here, but I'd have gone with _bar, whereas some would have suggested m_bar, and regardless, there's no way to indicate the name with this syntax, so you'd have to either just know how the compiler names such variables or statically disallow using the property through anything other than the proprty functions. And if all the property does is get and set with _nothing_ else, then how is that any better than a public member variable, assuming that switching between a variable and a property is seemless like it's supposed to be? So, I think that I'd still prefer the approach of making it so that marking variables property makes it so that you can only use them in ways that you can use a property function, since it gives you the same result without needing to actually create any functions or come up with naming schemes for implicit member variables or whatnot. - Jonathan M Davis
Aug 04 2012
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-08-04 21:28, Jonathan M Davis wrote:

 That would be kind of cool, though I would have suggested that putting
  property on a variable would make it so that you couldn't do anything with it
 that you couldn't do with a property (e.g. taking the address of a variable
 will break when it's switched to a property, and  property on the variable
 could prevent that). But maybe your proposal is better - though I'm not sure
 how much I'd end up using it, because if you wanted to actually use the member
 variable, you'd get into naming issues. You proposed bar_ here, but I'd have
 gone with _bar, whereas some would have suggested m_bar, and regardless,
 there's no way to indicate the name with this syntax, so you'd have to either
 just know how the compiler names such variables or statically disallow using
 the property through anything other than the proprty functions. And if all the
 property does is get and set with _nothing_ else, then how is that any better
 than a public member variable, assuming that switching between a variable and
 a property is seemless like it's supposed to be? So, I think that I'd still
 prefer the approach of making it so that marking variables  property makes it
 so that you can only use them in ways that you can use a property function,
 since it gives you the same result without needing to actually create any
 functions or come up with naming schemes for implicit member variables or
 whatnot.
I see two reasons: 1. The generated methods will be virtual 2. I'm thinking that it would be possible to override either the getter or setter. Meaning you would get one for free class Foo { property int bar: property int bar (int value) { validate(value); return bar_ = value; } } In the above code, only a getter will be generated. About the name of generate instance variable, we could add syntax making it possible to change the name: class Foo { property(name=m_bar) int bar: } -- /Jacob Carlborg
Aug 05 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-08-04 21:28, Jonathan M Davis wrote:

 That would be kind of cool, though I would have suggested that putting
  property on a variable would make it so that you couldn't do anything with it
 that you couldn't do with a property (e.g. taking the address of a variable
 will break when it's switched to a property, and  property on the variable
 could prevent that). But maybe your proposal is better - though I'm not sure
 how much I'd end up using it, because if you wanted to actually use the member
 variable, you'd get into naming issues. You proposed bar_ here, but I'd have
 gone with _bar, whereas some would have suggested m_bar, and regardless,
 there's no way to indicate the name with this syntax, so you'd have to either
 just know how the compiler names such variables or statically disallow using
 the property through anything other than the proprty functions. And if all the
 property does is get and set with _nothing_ else, then how is that any better
 than a public member variable, assuming that switching between a variable and
 a property is seemless like it's supposed to be? So, I think that I'd still
 prefer the approach of making it so that marking variables  property makes it
 so that you can only use them in ways that you can use a property function,
 since it gives you the same result without needing to actually create any
 functions or come up with naming schemes for implicit member variables or
 whatnot.
A few more reasons: 3. Binary compatibility 4. Read only fields. I quite often want to have read only fields and basically the only way to that is by using a getter property method. In D1 I could sometimes avoid this by declaring the instance variable as const, but that doesn't work in D2 -- /Jacob Carlborg
Aug 05 2012
prev sibling next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 August 2012 at 19:08:58 UTC, Jacob Carlborg wrote:
 I don't think that's correct behavior. I think the correct 
 behavior would be to have a property rewrite, something like 
 this:
Yes, I agree in general, but if a getter returns a ref, you should be able to write to it... I think anyway, but it is an lvalue anyway. What I just added to my hacked compiler is this: ===== else if(e1->op == TOKcall) { // for property work, if there is a setter, we should revert to the older style // handling. If not, we can keep it as a CallExp CallExp* ce = (CallExp*) e1; Expression* temp_e1 = ce->e1; if((temp_e1->op == TOKvar && temp_e1->type->toBasetype()->ty == Tfunction)) { // this is potentially a setter.... but not necessarily fd = ((VarExp *)temp_e1)->var->isFuncDeclaration(); ethis = NULL; assert(fd); FuncDeclaration *f = fd; Expressions a; a.push(e2); fd = f->overloadResolve(loc, ethis, &a, 1); if (fd && fd->type) { e1 = temp_e1; goto Lsetter; } } ===== To line 10320 - right above where the old style setter code is. It isn't perfect yet because it doesn't ensure we are dealing with a property, but it is closer. The idea is: if we have a setter function, we should try to use it, just like dmd does today. If not, we'll leave the call there and see what happens. (If it returns ref, it will work, otherwise, it errors saying the property is not an lvalue.) So far this is passing my simple test for assignment, but not yet the op assigns. int a; property int funcprop() { return a; } // setter property int funcprop(int s) { return a = s + 10; } funcprop = 10; // works, funcprop == 20 now funcprop += 10; // currently does NOT work
 Is rewritten as:

 auto __tmp = foo;
 foo = __tmp + 10;
I think this is exactly what we have to do to work in all cases. I'm gonna take a look at it next... then it is time to test this patch and with a little luck, we can finally put the property debate to rest.
 I think that you should always be able to replace a variable 
 with a property. The other way around I'm not so sure. The 
 problem is with methods in classes. Since a method will be 
 virtual by default you can't just replace a property with a 
 variable. That could potentially break subclasses that override 
 the property.
True. I think all the other uses should just work though.
Aug 04 2012
parent reply Jacob Carlborg <doob me.com> writes:
On 2012-08-04 21:25, Adam D. Ruppe wrote:

 Yes, I agree in general, but if a getter returns a ref, you should
 be able to write to it... I think anyway, but it is an lvalue anyway.
Or we could disallow getters returning by reference.
 What I just added to my hacked compiler is this:

 =====
   else if(e1->op == TOKcall) {
              // for property work, if there is a setter, we should
 revert to the older style
              // handling. If not, we can keep it as a CallExp
              CallExp* ce = (CallExp*) e1;
              Expression* temp_e1 = ce->e1;
              if((temp_e1->op == TOKvar &&
 temp_e1->type->toBasetype()->ty == Tfunction)) {
                  // this is potentially a setter.... but not necessarily
                  fd = ((VarExp *)temp_e1)->var->isFuncDeclaration();
                  ethis = NULL;
                  assert(fd);
                  FuncDeclaration *f = fd;
                  Expressions a;
                  a.push(e2);

                  fd = f->overloadResolve(loc, ethis, &a, 1);
                  if (fd && fd->type) {
                      e1 = temp_e1;
                      goto Lsetter;
                  }
              }
 =====


 To line 10320 - right above where the old style setter code is. It isn't
 perfect yet because it doesn't ensure we are dealing with a  property,
 but it is closer.



 The idea is: if we have a setter function, we should try to use it, just
 like dmd does today. If not, we'll leave the call there and see what
 happens. (If it returns ref, it will work, otherwise, it errors saying
 the property is not an lvalue.)


 So far this is passing my simple test for assignment, but not yet the op
 assigns.

 int a;
  property int funcprop() {
          return a;
 }

 // setter
  property int funcprop(int s) {
          return a = s + 10;
 }

 funcprop = 10; // works, funcprop == 20 now
Doesn't this already work?
 True. I think all the other uses should just work though.
Yes. Have a look at this for a workaround: http://forum.dlang.org/thread/gknbobkfhmfszshqenng forum.dlang.org#post-jvjs5j:248ov:241:40digitalmars.com -- /Jacob Carlborg
Aug 05 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 5 August 2012 at 12:14:41 UTC, Jacob Carlborg wrote:
 Doesn't this already work?
Yes, but it is in my test file to make sure it still works, without any additional pointless function calls. Right now the basics all work, but member template function that happen to be getters can break - this is the assertion failure I saw last night. I think my fix to implicit this calls broke it, ugh. And then I need to check the operator overloading.
 Yes. Have a look at this for a workaround:
I saw it, but one step at a time for now...
Aug 05 2012
parent Jacob Carlborg <doob me.com> writes:
On 2012-08-05 15:51, Adam D. Ruppe wrote:

 And then I need to check the operator overloading.
I see.
 I saw it, but one step at a time for now...
Hehe, yes, of course. No pressure :) -- /Jacob Carlborg
Aug 05 2012
prev sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 August 2012 at 19:08:58 UTC, Jacob Carlborg wrote:
 foo += 10;

 Is rewritten as:

 auto __tmp = foo;
 foo = __tmp + 10;
I did this and now I think this thing is almost done === int a; property int funcprop() { writeln("calling the getter and getting ", a); return a; } property int funcprop(int s) { writeln("calling the setter with ", s); return a = s; } void main() { funcprop = 40; funcprop += 10; } === run: calling the setter with 40 calling the getter and getting 40 calling the setter with 50 Looks right to me. Now I just have to check for the property tag on some of these rewrites and then clean it up and test for a pull request.
Aug 04 2012
next sibling parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
OK, I just compiled one of my work apps with the hacked up dmd.

It caught me using properties as functions in a few places, which 
I had to fix by removing the parenthesis from some places, and 
the  property from a few other places (the functions weren't 
really properties, I just marked them as such for the syntax...)


But hey if it compiles this thing with only a few, very minor 
changes, I think we're in business. We should be able to close 
those gazillion ancient property bugs once and for all, with 
minimal code breakage.

I'll push it up to github later tonight for additional review.
Aug 04 2012
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, August 04, 2012 23:35:47 Adam D. Ruppe wrote:
 OK, I just compiled one of my work apps with the hacked up dmd.
 
 It caught me using properties as functions in a few places, which
 I had to fix by removing the parenthesis from some places, and
 the  property from a few other places (the functions weren't
 really properties, I just marked them as such for the syntax...)
 
 
 But hey if it compiles this thing with only a few, very minor
 changes, I think we're in business. We should be able to close
 those gazillion ancient property bugs once and for all, with
 minimal code breakage.
 
 I'll push it up to github later tonight for additional review.
If you can get properties working properly that quickly, that's fantastic. - Jonathan M Davis
Aug 04 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 August 2012 at 21:47:44 UTC, Jonathan M Davis 
wrote:
 If you can get properties working properly that quickly, that's 
 fantastic.
I did just realize I missed something.... but I'm convinced the problem before is we were going at it at the wrong time, doing the property rewrites too late, meaning it had to be duplicated a lot and was hard to check fully. My approach here is to rewrite the properties almost immediately into function calls instead of leaving them as just function references until the last minute. (And then in a couple places, they need to be turned back into references so you can look up the setter, but I say better to only need two special cases than twenty.)
Aug 04 2012
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2012-08-04 23:35, Adam D. Ruppe wrote:
 OK, I just compiled one of my work apps with the hacked up dmd.

 It caught me using properties as functions in a few places, which I had
 to fix by removing the parenthesis from some places, and the  property
 from a few other places (the functions weren't really properties, I just
 marked them as such for the syntax...)


 But hey if it compiles this thing with only a few, very minor changes, I
 think we're in business. We should be able to close those gazillion
 ancient property bugs once and for all, with minimal code breakage.

 I'll push it up to github later tonight for additional review.
Awesome :) -- /Jacob Carlborg
Aug 05 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-08-04 22:53, Adam D. Ruppe wrote:

 I did this and now I think this thing is almost done

 ===
 int a;
  property int funcprop() {
          writeln("calling the getter and getting ", a);
      return a;
 }
  property int funcprop(int s) {
      writeln("calling the setter with ", s);
      return a = s;
 }
 void main() {
      funcprop = 40;
      funcprop += 10;
 }
 ===

 run:

 calling the setter with 40
 calling the getter and getting 40
 calling the setter with 50

 Looks right to me. Now I just have to check for the  property tag on
 some of these rewrites and then clean it up and test for a pull request.
BTW, how will this interact with overloaded operators and similar more advanced features? Say that I have a getter that returns a struct/class which overloads the += operator. -- /Jacob Carlborg
Aug 05 2012
parent "Adam D. Ruppe" <destructionator gmail.com> writes:
On Sunday, 5 August 2012 at 12:18:52 UTC, Jacob Carlborg wrote:
 BTW, how will this interact with overloaded operators and 
 similar more advanced features? Say that I have a getter that 
 returns a struct/class which overloads the += operator.
I'm not sure yet... stuff like this is one reason why it didn't work out right last night. (I have it working in about 90% of cases, but it is interaction with those last advanced features that always keep the D bugzilla active.) With the rewrite right now, it does getter -> opOpAssign -> setter, which isn't quite right...
Aug 05 2012
prev sibling parent "Michael" <pr m1xa.com> writes:
On Saturday, 4 August 2012 at 20:53:53 UTC, Adam D. Ruppe wrote:
 On Saturday, 4 August 2012 at 19:08:58 UTC, Jacob Carlborg 
 wrote:
 foo += 10;

 Is rewritten as:

 auto __tmp = foo;
 foo = __tmp + 10;
I did this and now I think this thing is almost done === int a; property int funcprop() { writeln("calling the getter and getting ", a); return a; } property int funcprop(int s) { writeln("calling the setter with ", s); return a = s; } void main() { funcprop = 40; funcprop += 10; } === run: calling the setter with 40 calling the getter and getting 40 calling the setter with 50 Looks right to me. Now I just have to check for the property tag on some of these rewrites and then clean it up and test for a pull request.
It would be cool http://forum.dlang.org/thread/xcbweciovapinaicxgbn forum.dlang.org and http://d.puremagic.com/issues/show_bug.cgi?id=8006
Aug 06 2012
prev sibling next sibling parent reply Chad J <chadjoan __spam.is.bad__gmail.com> writes:
There was a very long discussion on this before that lead me to write this:

http://www.prowiki.org/wiki4d/wiki.cgi?DocComments/Property

I should draw attention to the tables near the bottom.  It is really too 
bad that variables in D allow the taking of addresses by default.  If 
things were non-addressable by default, then they could always be 
promoted to properly-implemented properties.  Addressabilitiy could be 
added explicitly by authors that are confident that they will never need 
to turn their public fields into properties.  Too late for all of that 
now.

I tried to get DMD to do property rewriting in a vaguely general sense. 
  I think it's still very worthwhile to attempt it.

It didn't work for me because this was in the days of SVN and I tend to 
get my free time in 1-3 hour chunks.  By the time I had more free time 
I'd spend it all merging/updating my code to the latest revision.  I 
also spent a lot of my time learning how DMD's semantic analysis works. 
  It's pretty tangled (sorry, Walter).  So yeah, I didn't get to spend 
much time actually working on the property rewrite due to confusion and 
backtracking.  I realized that while I really wanted the feature, I 
wasn't having fun, nor did I have any free time to program at all. 
(Implementing the recursive logic for such a rewrite is really easy! 
Dealing with DMDs architectural corner cases... (was) not so easy.)

It'd probably be good to be minimal about it.  We might not be able to 
do property rewriting in a %100 awesome way without breaking backwards 
compatibility.  It'll still be worth it to get the property rewriting 
that doesn't break backwards compatibility.  Example:

struct Bar
{
	private Foo m_a;
	
	 property Foo a()
	{
		return m_a;
	}
}

struct Foo
{
	private int qux;

	Foo opBinary(string op : "+")(Foo other)
	{
		Foo result;
		result.qux = this.qux + other.qux
		return result;
	}
}

void main()
{
	Bar a;
	Foo b;

	// Error: attempt to write to a non-writable property a.a
	b = a.a + b;
}

To make the property rewrite bulletproof, we have to assume that (a.f + 
b) will modify a.a because a.a.opBinary!("+") is non-const.  We should 
then compile-time-error because the property a.a does not have a setter.
However, in current code, this would not give a compile-time error. 
Instead it would just work, because the caller and callee implicitly 
assume that the opBinary call is const, even if it's not marked as such. 
  There is probably a significant amount of code that would no longer 
compile because of this.  The answer would be to mark all such functions 
as const, but I'm not sure that Walter & Co would be fine with that.

It's OK though.  If Walter & Co don't want to break various calls to 
non-const functions at the end of expressions containing properties, 
then we can let that slide and only focus on the more likely cases like 
using += on a property field that returns a value type like an int 
(array.length += 1 is the classic case, with its own special-cased 
solution and everything).  It'd be extremely useful and possibly remove 
a lot of potential sources of bugs/headaches.

Anyhow, that's a lot of my background info on this subject.  I do hope 
someone else tackles this.  They will probably be more successful than 
I.  If not, I might still try it someday if the pull request might get 
accepted.
Aug 04 2012
parent reply "Adam D. Ruppe" <destructionator gmail.com> writes:
On Saturday, 4 August 2012 at 22:19:17 UTC, Chad J wrote:
 There was a very long discussion on this before that lead me to 
 write this:
I'll have to read it... my thing right now works, as far as I can tell, perfectly on free function properties. But member properties are different and I haven't gotten them to work without massive breakage yet. (I forgot all about them until just an hour ago.) For some reason, rewriting the members breaks random things like opCall too. The good is the free function change doesn't actually break anything... the bad is member properties are the more interesting ones and if they don't work, this isn't cool.... But it doesn't look like I'll be done with this today after all :( I still have a lot of stuff I was supposed to be doing today!
Aug 04 2012
parent Chad J <chadjoan __spam.is.bad__gmail.com> writes:
On 08/04/2012 06:50 PM, Adam D. Ruppe wrote:
 On Saturday, 4 August 2012 at 22:19:17 UTC, Chad J wrote:
 There was a very long discussion on this before that lead me to write
 this:
I'll have to read it... my thing right now works, as far as I can tell, perfectly on free function properties. But member properties are different and I haven't gotten them to work without massive breakage yet. (I forgot all about them until just an hour ago.) For some reason, rewriting the members breaks random things like opCall too. The good is the free function change doesn't actually break anything... the bad is member properties are the more interesting ones and if they don't work, this isn't cool.... But it doesn't look like I'll be done with this today after all :( I still have a lot of stuff I was supposed to be doing today!
Best of luck, and thank you for thinking about this and getting the ball rolling!
Aug 04 2012
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Monday, August 06, 2012 23:44:21 Jos=C3=A9 Armando Garc=C3=ADa Sanci=
o wrote:
 Wow. That would be nice! I would like to see such a change though I
 assume that such a change would break a lot of code. No?
No. Both the string syntax and the new lambda syntax are here to stay. = Both=20 work now, and both will continue to work. The pull request was switchin= g over=20 most of the examples, not changing the functionality. - Jonathan M Davis
Aug 06 2012