www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Do we really need const?

reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bruce Adams wrote:
 charles Wrote:

 Const is a god awful abomination that no language has gotten right, 
 the sooner its gotten rid of the better.  No one I work with has 
ever >> had a situation where const actually paid off.

 As a seasoned C++ programmer I and my colleagues regularly use the
 c++ flavour of const with no problems. It may not be the best 
 solution in the world but it works. I always found it a reasonably
 intuitive bit of syntactic sugar. That's not to say that we can't do
 better in D. I'd recommend reading Scott Meyers effective C++ books
 for a start. And perhaps we should review the deliberations that went
 into developing the C++ const system. 
 By the way, what languages other than C++ have some kind of const
 mechanism? They've got to be worth a review too.
That's a very good point. *Are* there any others? Pascal seems to have some form of it (http://www.gnu-pascal.de/gpc/const.html). Any Pascal devotees out there care to comment? I managed to avoid ever learning Pascal. Anyway, if one asserts that const is a critical feature for "enterprise code", yet there exist a large body of "enterprise code" written in languages that don't have const, then we have a contradiction. It can't be all that critical if lots of successful software projects get written without it. There's of course Java. Lots of enterprise software is written in Java, and yet Java does not have const. There is without a doubt some diagnostic benefit to const, but if it were such a great win overall wouldn't everybody be doing it? Is it really worth the pain of having to create two versions of every function, and two flavors of every iterator, etc.? I guess I've gotten used to it in C++ and certainly bought into the const-correctness mantra. When I see C++ code that doesn't use const correctly my kneejerk reaction is always "sheesh -- amateur". But I've never really taken a step back to try to seriously evaluate the cost-benefit tradeoff. It seems from the discussion here the past week, there is no real multithreading benefit to be had from const/invariant. 'Pure' is where it's at for that. So maybe we're just better off without the complexities of const. I've certainly gotten used to the lack of const in Python, so why not in a C++-ish language? --bb
Sep 16 2007
next sibling parent reply Don Clugston <dac nospam.com.au> writes:
Bill Baxter wrote:
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the 
 complexities of const.
I'm thinking the same way. There are clear benefits to 'pure', with both multi-threading and optimisation, and it's conceptually simple and easy to explain to a newbie. But 'const' seems to be a horrible morass, complicated to explain, complicated to code, and with very little benefit. At the very least, it's clear that 'const' has cost the D community a huge amount of discussion bandwidth and mental space, with very little of use to show for it. We may be caught in a productivity trap.
Sep 17 2007
parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Don Clugston Wrote:

 Bill Baxter wrote:
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the 
 complexities of const.
I'm thinking the same way. There are clear benefits to 'pure', with both multi-threading and optimisation, and it's conceptually simple and easy to explain to a newbie. But 'const' seems to be a horrible morass, complicated to explain, complicated to code, and with very little benefit. At the very least, it's clear that 'const' has cost the D community a huge amount of discussion bandwidth and mental space, with very little of use to show for it. We may be caught in a productivity trap.
From what I've seen here. Talking in this group all day long doesn't seem to affect productivity that badly. ;)
Sep 17 2007
prev sibling next sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

 Bruce Adams wrote:
  > charles Wrote:
  >
  >> Const is a god awful abomination that no language has gotten right, 
  >> the sooner its gotten rid of the better.  No one I work with has 
 ever >> had a situation where const actually paid off.
  >>
 
 As a seasoned C++ programmer I and my colleagues regularly use the
 c++ flavour of const with no problems. It may not be the best 
 solution in the world but it works. I always found it a reasonably
 intuitive bit of syntactic sugar. That's not to say that we can't do
 better in D. I'd recommend reading Scott Meyers effective C++ books
 for a start. And perhaps we should review the deliberations that went
 into developing the C++ const system. 
 By the way, what languages other than C++ have some kind of const
> mechanism? They've got to be worth a review too. That's a very good point. *Are* there any others? Pascal seems to have some form of it (http://www.gnu-pascal.de/gpc/const.html). Any Pascal devotees out there care to comment? I managed to avoid ever learning Pascal. Anyway, if one asserts that const is a critical feature for "enterprise code", yet there exist a large body of "enterprise code" written in languages that don't have const, then we have a contradiction. It can't be all that critical if lots of successful software projects get written without it. There's of course Java. Lots of enterprise software is written in Java, and yet Java does not have const. There is without a doubt some diagnostic benefit to const, but if it were such a great win overall wouldn't everybody be doing it? Is it really worth the pain of having to create two versions of every function, and two flavors of every iterator, etc.? I guess I've gotten used to it in C++ and certainly bought into the const-correctness mantra. When I see C++ code that doesn't use const correctly my kneejerk reaction is always "sheesh -- amateur". But I've never really taken a step back to try to seriously evaluate the cost-benefit tradeoff. It seems from the discussion here the past week, there is no real multithreading benefit to be had from const/invariant. 'Pure' is where it's at for that. So maybe we're just better off without the complexities of const. I've certainly gotten used to the lack of const in Python, so why not in a C++-ish language? --bb
Arguing that something isn't useful because you can live without it is not sound. I can live without a dish-washer but I'd rather not have to do the washing up myself. Similarly the GIT people seem perfectly happy to write all their code in C, and from my brief glance at their code base its quite neat. Its a case of working smarter versus working harder. That said, its better to wait and deliberate and get a good const system rather than live with something that's not up to par. I can't really comment on pascal myself. I did do some modula-2 many years ago, which I consider to be 2 steps on from pascal in Wirths evolution (pascal, modula 1,2,3 then oberon). I think const is just used for variables assigned once and ideally at compile time when the expression permits it. Regards, Bruce.
Sep 17 2007
prev sibling next sibling parent reply Matti Niemenmaa <see_signature for.real.address> writes:
Bill Baxter wrote:
 There's of course Java.  Lots of enterprise software is written in Java,
 and yet Java does not have const.
Java has a limited form of const in final, which allows for both compile-time constants and variables which can only be assigned to once. See the "variables" and "fields" sections here: http://renaud.waldura.com/doc/java/final-keyword.shtml -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 17 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Matti Niemenmaa wrote:
 Bill Baxter wrote:
 There's of course Java.  Lots of enterprise software is written in Java,
 and yet Java does not have const.
Java has a limited form of const in final, which allows for both compile-time constants and variables which can only be assigned to once. See the "variables" and "fields" sections here: http://renaud.waldura.com/doc/java/final-keyword.shtml
Yes, a lot of languages seem to have that form of const, which is more or less like const is in D1.x. That doesn't seem to be the problematic kind. --bb
Sep 17 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Bill Baxter wrote:
 Matti Niemenmaa wrote:
 Bill Baxter wrote:
 There's of course Java.  Lots of enterprise software is written in Java,
 and yet Java does not have const.
Java has a limited form of const in final, which allows for both compile-time constants and variables which can only be assigned to once. See the "variables" and "fields" sections here: http://renaud.waldura.com/doc/java/final-keyword.shtml
Yes, a lot of languages seem to have that form of const, which is more or less like const is in D1.x. That doesn't seem to be the problematic kind.
Java also has invariant strings which is another form of const, one that makes multithreading easier. It seems that there are at least 4 forms of const: 1. invariant data 2. invariant variables 3. logical const 4. transitive const Forms #1 and #2 should combine well with 'pure' I imagine. Regan
Sep 17 2007
parent Regan Heath <regan netmail.co.nz> writes:
Regan Heath wrote:
 2. invariant variables
This was badly named, I really mean "invariant references/pointers". Some might call this an extension of #1 (invariant data) Regan
Sep 17 2007
prev sibling parent reply Matti Niemenmaa <see_signature for.real.address> writes:
Bill Baxter wrote:
 Matti Niemenmaa wrote:
 Java has a limited form of const in final, which allows for both 
 compile-time constants and variables which can only be assigned to once.
 
 See the "variables" and "fields" sections here: 
 http://renaud.waldura.com/doc/java/final-keyword.shtml
Yes, a lot of languages seem to have that form of const, which is more or less like const is in D1.x. That doesn't seem to be the problematic kind.
No. But it's more than D1.x's const, in that it can be used other than just for compile time constants. -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 17 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Matti Niemenmaa wrote:
 Bill Baxter wrote:
 Matti Niemenmaa wrote:
 Java has a limited form of const in final, which allows for both 
 compile-time constants and variables which can only be assigned to once.

 See the "variables" and "fields" sections here: 
 http://renaud.waldura.com/doc/java/final-keyword.shtml
Yes, a lot of languages seem to have that form of const, which is more or less like const is in D1.x. That doesn't seem to be the problematic kind.
No. But it's more than D1.x's const, in that it can be used other than just for compile time constants.
It's like D2.0's 'final' then. Regan
Sep 17 2007
parent Matti Niemenmaa <see_signature for.real.address> writes:
Regan Heath wrote:
 Matti Niemenmaa wrote:
 Bill Baxter wrote:
 Matti Niemenmaa wrote:
 Java has a limited form of const in final, which allows for both 
 compile-time constants and variables which can only be assigned to 
 once.
 
 See the "variables" and "fields" sections here: 
 http://renaud.waldura.com/doc/java/final-keyword.shtml
Yes, a lot of languages seem to have that form of const, which is more or less like const is in D1.x. That doesn't seem to be the problematic kind.
No. But it's more than D1.x's const, in that it can be used other than just for compile time constants.
It's like D2.0's 'final' then.
Yep. -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Sep 17 2007
prev sibling next sibling parent Lionello Lunesu <lio lunesu.remove.com> writes:
Just a quick note to say that I also have never missed "const" in D and 
am reluctant to start with D 2.0 because of the added complexity. Walter 
has said himself that while porting Phobos to 2.0, he has not 
encountered, let alone fixed, a single bug. For me, 
const/final/invariant is just not worth it.

I like const conceptually, but then there are many similar concepts that 
are nice and const is just one of them.

D should opt for a more general red code / green code mechanism, as 
explained by Scott Meyers in the next video:

http://video.google.com/videoplay?docid=-4728145737208991310

Pure/not-pure, throw/no-throw, const/not-const, 
threadsafe/not-threadsafe etc. are all similar and I would love to see 
one solution for all these red/green restrictions.

L.

PS. I'm playing around with the idea to add keywords after a function 
declaration to add labels to the function. The compiler should then only 
accept calls to functions with at least the labels of the caller:

void bar() throw;
void foo() { bar(); }//ERROR, bar can throw
void foo2() throw { bar(); } //OK
void foo3() throw { bar() throw; } //OK ("cast")

Or something to that effect. Possible using "!" to negate (!throw). I 
have not thought this through more than what's written here and have no 
idea what it would imply for the grammar.
Sep 17 2007
prev sibling next sibling parent reply Jan Claeys <usenet janc.be> writes:
Op Mon, 17 Sep 2007 15:36:35 +0900
schreef Bill Baxter <dnewsgroup billbaxter.com>:

 I've certainly gotten used to the lack of const in Python
Python has immutable objects (e.g. strings & tuples). :) -- JanC
Sep 17 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jan Claeys wrote:
 Op Mon, 17 Sep 2007 15:36:35 +0900
 schreef Bill Baxter <dnewsgroup billbaxter.com>:
 
 I've certainly gotten used to the lack of const in Python
Python has immutable objects (e.g. strings & tuples). :)
Yeh I'm really thinking more about the type of const that appears in front of parameters to functions, and forces you to write all your member functions twice in C++. Whatever you call that form. #3 & #4 from Regan's list. :-) --bb
Sep 17 2007
prev sibling next sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Jan Claeys wrote:
 Op Mon, 17 Sep 2007 15:36:35 +0900
 schreef Bill Baxter <dnewsgroup billbaxter.com>:
 
 I've certainly gotten used to the lack of const in Python
Python has immutable objects (e.g. strings & tuples). :)
A lot of Python users also seem to be quite unhappy about the gil. Python in its current form appears to be unsuitable for writing parallel code.
Sep 17 2007
parent reply Jan Claeys <usenet janc.be> writes:
Op Mon, 17 Sep 2007 23:00:33 -0700
schreef Walter Bright <newshound1 digitalmars.com>:

 A lot of Python users also seem to be quite unhappy about the gil.
Actually, only some Python implementation have the GIL (CPython & Stackless), most don't have it (Jython, IronPython, PyPy, etc.). Of course CPython is probably used more than all others combined...
 Python in its current form appears to be unsuitable for writing
 parallel code.
There are lots of (sometimes massively) parallel python applications around; some of them were named in the recent discussions. As an example, the EVE Online servers & clients work as one large distributed parallel application (there is one shared "state"). The complaints seem to be more about how people have to write parallel code, but trying to port Java/C++ code/idioms word for word into Python (obviously) doesn't work very well. AFAIK most CPython parallel programming uses one of two techniques: 1. Non-Python modules (e.g. those written in C, C++ or D) can (temporarily) release the GIL if they make sure they don't access/change data shared with other threads during that period. 2. Multiple processes + some form of IPC (machine-local or distributed over a cluster) -- JanC
Sep 22 2007
parent reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Jan Claeys wrote:
 Op Mon, 17 Sep 2007 23:00:33 -0700
 schreef Walter Bright <newshound1 digitalmars.com>:
 Python in its current form appears to be unsuitable for writing
 parallel code.
There are lots of (sometimes massively) parallel python applications around; some of them were named in the recent discussions. As an example, the EVE Online servers & clients work as one large distributed parallel application (there is one shared "state").
To be fair, that's parallelism via stackless which is just cooperative multithreading. As far as I understand it, if you ignore the overhead of context switching and locks, code written against stackless and using Python threads will run at the same speed irrespective of how many hardware threads you have. Neither one, AFAIK, can actually execute multiple threads of Python code simultaneously. That said, stackless is still the bee's knees if you're on a uniprocessor machine. The ability to have hundreds of thousands of super lightweight threads with almost no overhead, *and* the ability to just pickle a thread and re-start it on another machine? And people wonder why the EVE guys get so excited by it :P -- Daniel
Sep 22 2007
parent Jan Claeys <usenet janc.be> writes:
Op Sun, 23 Sep 2007 16:16:03 +1000
schreef Daniel Keep <daniel.keep.lists gmail.com>:

 Jan Claeys wrote:
 There are lots of (sometimes massively) parallel python applications
 around; some of them were named in the recent discussions.  As an
 example, the EVE Online servers & clients work as one large
 distributed parallel application (there is one shared "state").
To be fair, that's parallelism via stackless which is just cooperative multithreading. As far as I understand it, if you ignore the overhead of context switching and locks, code written against stackless and using Python threads will run at the same speed irrespective of how many hardware threads you have. Neither one, AFAIK, can actually execute multiple threads of Python code simultaneously.
Not multiple threads of pure python code, but modules written in C++ (which I think they use at least in the client, and most likely also server-side) can use multiple simultaneous threads. And multiple processes + IPC are no problem for pure python code.
 That said, stackless is still the bee's knees if you're on a
 uniprocessor machine.  The ability to have hundreds of thousands of
 super lightweight threads with almost no overhead, *and* the ability
 to just pickle a thread and re-start it on another machine?
They run it on a cluster of several multiprocessor machines, not even counting the tens of thousands of PCs of their customers (the players of the game) that have to share (part of) the state of the game. <http://myeve.eve-online.com/devblog.asp?a=blog&bid=303> tells about their early 2006 hardware (70 dual-cpu IBM LS20 Opteron blades :) ). On 2007-09-03 they had more than 35000 people logged in into the same game: <http://www.gamershell.com/news/41371.html>. -- JanC
Sep 24 2007
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Jan Claeys wrote:
 Op Mon, 17 Sep 2007 15:36:35 +0900
 schreef Bill Baxter <dnewsgroup billbaxter.com>:
 
 I've certainly gotten used to the lack of const in Python
Python has immutable objects (e.g. strings & tuples). :)
Actually I remembered one time when lack of const did bite me in Python. I was translating some Matlab code I wrote to Python/NumPy (http://www.scipy.org/). In Matlab all calls are call-by-value, and all assignments are assign-by-value (both use COW under the covers to make performance reasonable). In Python they're call-by-reference and assign-by-reference for most anything more complicated than an 'int'. The matrix class I was using in the Python code definitely fell into the by-reference category. After I got the port basically working there were quite a few bugs that turned out to be a result of functions I translated from Matlab modifying their arguments. If Python had const, I don't think I would have had those problems. On the other hand, the NumPy matrix class itself has a read-only flag. If I were porting to Python again I'd probably try to write a decorator to use during debugging that sets that readonly flag before entering the function and unsets it on return. --bb
Sep 22 2007
parent Jan Claeys <usenet janc.be> writes:
Op Sun, 23 Sep 2007 15:48:08 +0900
schreef Bill Baxter <dnewsgroup billbaxter.com>:

 Actually I remembered one time when lack of const did bite me in
 Python. I was translating some Matlab code I wrote to Python/NumPy 
 (http://www.scipy.org/).

 In Matlab all calls are call-by-value, and all assignments are 
 assign-by-value (both use COW under the covers to make performance 
 reasonable).  In Python they're call-by-reference and 
 assign-by-reference for most anything more complicated than an 'int'. 
 The matrix class I was using in the Python code definitely fell into
 the by-reference category.
Never do a word-by-word translation... :) Both mutable & immutable objects are passed as "objects" in python (the technical details of how that happens, e.g. using pointers, is merely an implementation detail, but it's obviously very close to what happens with call-by-reference in other languages) and it's only at the time of assignment that behaviour between them differs. It's the same outside of a function/method and has nothing to do with the different methods of parameter passing in other languages.
 If Python had const, I don't think I would have had those problems.
Well, Python has something 'const'-like, only it's linked to the object's type, and not defined by the way it's called. ;) And if you want to "call by value", there is always the 'copy' module: <http://docs.python.org/lib/module-copy.html> (Could be used in a decorator too...) -- JanC
Sep 24 2007
prev sibling next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
news:fcl79k$18vu$1 digitalmars.com...

 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the complexities 
 of const.  I've certainly gotten used to the lack of const in Python, so 
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
Sep 17 2007
next sibling parent reply Christopher Wright <dhasenan gmail.com> writes:
Jarrett Billingsley wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:fcl79k$18vu$1 digitalmars.com...
 
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the complexities 
 of const.  I've certainly gotten used to the lack of const in Python, so 
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++.
I think it would be a comfort to have it for function signatures, but I've only been affected by it once -- with getopt on C. (And getopt is an ugly, horrible thing. _Why_ does it modify the command line arguments?) Other than that, D2 invariant and final seem useful. D2 const is good for basic things, but seems to scale poorly.
 Never having learned a const-correct language (started with various BASICs, 
 then on to very rudimentary C++, then D, and also a bit of Java and C#), I 
 simply don't see the overwhelming need.  Sure, there are a very few cases 
 where const could be useful in my code for efficiency's sake, but I've never 
 felt like I was missing any expressive power.  Furthermore, I have been 
 bitten exactly once by something that theoretically could have been 
 prevented by const.  It took me two minutes in the debugger to track down 
 the problem.  I'd say that those two minutes were a much better tradeoff 
 than the god-knows-how-long it'd take to write const and non-const overloads 
 of who-knows-how-many functions. 
Most of the time, you shouldn't need to write those overloads. Either the function can be const, in which case you can use it with mutable data anyway, or it can't, in which case you can't write a const overload for it. At least in an ideal system.
Sep 17 2007
parent reply Walter Bright <newshound1 digitalmars.com> writes:
Christopher Wright wrote:
 Most of the time, you shouldn't need to write those overloads. Either 
 the function can be const, in which case you can use it with mutable 
 data anyway, or it can't, in which case you can't write a const overload 
 for it. At least in an ideal system.
The reason that C++ member functions are written twice, const and non-const, are to transmit the const to the return type: const T foo(const S); T foo(S); Nothing more.
Sep 17 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Christopher Wright wrote:
 Most of the time, you shouldn't need to write those overloads. Either 
 the function can be const, in which case you can use it with mutable 
 data anyway, or it can't, in which case you can't write a const 
 overload for it. At least in an ideal system.
The reason that C++ member functions are written twice, const and non-const, are to transmit the const to the return type: const T foo(const S); T foo(S); Nothing more.
There's also the practice of writing all iterators twice. One tail const, one tail mutable. --bb
Sep 17 2007
prev sibling next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
Jarrett Billingsley Wrote:

 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:fcl79k$18vu$1 digitalmars.com...
 
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the complexities 
 of const.  I've certainly gotten used to the lack of const in Python, so 
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
You put it a lot better than I did. High-five!
Sep 17 2007
prev sibling next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Jarrett Billingsley wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:fcl79k$18vu$1 digitalmars.com...
 
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  
I was a little over-zealous in my condemnation here. I should have said just "const" not "const/invariant". --bb
Sep 18 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/17/07, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message
 news:fcl79k$18vu$1 digitalmars.com...

 It seems from the discussion here the past week, there is no real
 multithreading benefit to be had from const/invariant.  'Pure' is where
 it's at for that.  So maybe we're just better off without the complexities
 of const.  I've certainly gotten used to the lack of const in Python, so
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
Let me share some real world experience about using const versus not using const. Today, I found myself writing a small standalone project in D, for my own personal use. I wrote a function that took a ubtye[] as it's parameter. It occured to me that the function didn't actually modify the array, so (since I have D2.0) I could have declared the parameter const. I considered it, and then I thought: "Nah... let's not bother. Too much effort". BUT... When working on a big project, it's a different story. A couple of years back, I was team leader in a C++ project with half a dozen other developers. It was a multithreaded application. So I wrote this bunch of classes which had to be called in a certain way or it wouldn't compile - because data had to be shared between server instances and so forth. Well, a couple of the developers figured out the right thing to do all by themselves, and that was great. Of the others, about three tried the "obvious", discovered that it wouldn't compile because of const-correctness, and came to me asking why not. I explained that there was good reason for the constness, that it was thread-safety issue, and then I told them the correct, "authorised" way of doing things, and then they went off and did the right thing. So in this case, const forced everyone to get it right. Well - almost everyone. (There's always /one/!) One team member tried the "obvious", discovered that it wouldn't compile, and then, instead of asking me why not, figured out a workaround to make it compile using pointers. (If C++ had had intransitive const, the compiler would have prevented this, but it doesn't so he got away with it). Then ... weeks later ... we found that the server randomly fell over after several hours of problem-free up-time. The symptoms were random memory corruption. It took us about a month to find and fix the problem! Care to guess what the problem turned out to be? Yes, it was that errant team member's workaround for const-correctness that ended up causing multiple threads to (rarely) become thread-unsafe. (Moral: If your code won't compile when you try to call a co-worker's function, don't hack up a workaround - instead, ask them if you're calling it right!) So what I conclude from this is: (1) For a small project, you can ignore const. Just because the language has it, doesn't mean you have to use it. (2) For a large project, it's essential. And moreover, it needs to be transitive by default. I don't have a problem with const being transitive by default. I think it's a good idea. I do, however, have a problem with there being *no way to specify* mutable pointer to const pointer to mutable data. We've listed enough use cases to know that sometimes that's exactly what you need. It shouldn't be the default, but it should be /possible/. But ... on the other hand ... we /can/ cast away const, and even though it's "undefined", there is some indication that you can get away with it if you know what you're doing (in terms of thread-safety), so maybe it's not as big a problem as some of us imagine. Anyway, there's my story and my conclusions. Hope it helps. If not, hope it kept you amused. :-)
Sep 19 2007
next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Janice Caron" wrote
 (If C++ had had intransitive const, the compiler would have prevented
 this, but it doesn't so he got away with it).
Something tells me that this friend of yours would have done the same thing in D. Is there a warning if you cast away const? If not, there should be. Otherwise, D wouldn't have helped you in this case either...
 I don't have a problem with const being transitive by default. I think
 it's a good idea. I do, however, have a problem with there being *no
 way to specify* mutable pointer to const pointer to mutable data.
 We've listed enough use cases to know that sometimes that's exactly
 what you need. It shouldn't be the default, but it should be
 /possible/.

 But ... on the other hand ... we /can/ cast away const, and even
 though it's "undefined", there is some indication that you can get
 away with it if you know what you're doing (in terms of
 thread-safety), so maybe it's not as big a problem as some of us
 imagine.
Then why be SO adamant about const being transitive! (i.e. Walter's view, not yours). If the 'accepted' practice is to cast away const if you want to have intransitive const, then transitive const is equally meaningless. The more I think about this issue, the more I think that I can probably live with transitive only const. I don't think it's such a bad thing as it simply makes you think differently when designing classes. As I believe intransitive const is a subset of transitive const, it's almost implementable with transitive-only, but I think I'll probably stay away from it. -Steve
Sep 19 2007
parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/19/07, Steven Schveighoffer <schveiguy yahoo.com> wrote:
 Something tells me that this friend of yours would have done the same thing
 in D.  Is there a warning if you cast away const?  If not, there should be.

 Otherwise, D wouldn't have helped you in this case either...
Difficult to say. Everyone hesitates before typing "const_cast"! Even when it's legitimate, it feels a bit naughty. Trouble is, the "const_cast" keyword is not compulsory. With C++ is that it isn't immediately obvious that a cast like "(void *)p" is casting away const, but that's what everyone types, because it's quicker than than the safe alternative "static_cast<void *>(p)". That's why I'm hoping Walter will arrange it so that D's "cast(T)" will refuse to cast away constness without a special syntax.
Sep 19 2007
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Janice Caron Wrote:

 On 9/17/07, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message
 news:fcl79k$18vu$1 digitalmars.com...

 It seems from the discussion here the past week, there is no real
 multithreading benefit to be had from const/invariant.  'Pure' is where
 it's at for that.  So maybe we're just better off without the complexities
 of const.  I've certainly gotten used to the lack of const in Python, so
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
Let me share some real world experience about using const versus not using const. Today, I found myself writing a small standalone project in D, for my own personal use. I wrote a function that took a ubtye[] as it's parameter. It occured to me that the function didn't actually modify the array, so (since I have D2.0) I could have declared the parameter const. I considered it, and then I thought: "Nah... let's not bother. Too much effort". BUT... When working on a big project, it's a different story. A couple of years back, I was team leader in a C++ project with half a dozen other developers. It was a multithreaded application. So I wrote this bunch of classes which had to be called in a certain way or it wouldn't compile - because data had to be shared between server instances and so forth. Well, a couple of the developers figured out the right thing to do all by themselves, and that was great. Of the others, about three tried the "obvious", discovered that it wouldn't compile because of const-correctness, and came to me asking why not. I explained that there was good reason for the constness, that it was thread-safety issue, and then I told them the correct, "authorised" way of doing things, and then they went off and did the right thing. So in this case, const forced everyone to get it right. Well - almost everyone. (There's always /one/!) One team member tried the "obvious", discovered that it wouldn't compile, and then, instead of asking me why not, figured out a workaround to make it compile using pointers. (If C++ had had intransitive const, the compiler would have prevented this, but it doesn't so he got away with it). Then ... weeks later ... we found that the server randomly fell over after several hours of problem-free up-time. The symptoms were random memory corruption. It took us about a month to find and fix the problem! Care to guess what the problem turned out to be? Yes, it was that errant team member's workaround for const-correctness that ended up causing multiple threads to (rarely) become thread-unsafe. (Moral: If your code won't compile when you try to call a co-worker's function, don't hack up a workaround - instead, ask them if you're calling it right!) So what I conclude from this is: (1) For a small project, you can ignore const. Just because the language has it, doesn't mean you have to use it. (2) For a large project, it's essential. And moreover, it needs to be transitive by default.
But wait... if there wasn't any const in the first place, the guy wouldn't have needed to hack up that workaround... I'm sensing a different moral here... ;-P
 
 I don't have a problem with const being transitive by default. I think
 it's a good idea. I do, however, have a problem with there being *no
 way to specify* mutable pointer to const pointer to mutable data.
 We've listed enough use cases to know that sometimes that's exactly
 what you need. It shouldn't be the default, but it should be
 /possible/.
 
 But ... on the other hand ... we /can/ cast away const, and even
 though it's "undefined", there is some indication that you can get
 away with it if you know what you're doing (in terms of
 thread-safety), so maybe it's not as big a problem as some of us
 imagine.
 
 Anyway, there's my story and my conclusions. Hope it helps. If not,
 hope it kept you amused. :-)
Sep 19 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Robert Fraser wrote:
 Janice Caron Wrote:
 
 On 9/17/07, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:
 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message
 news:fcl79k$18vu$1 digitalmars.com...

 It seems from the discussion here the past week, there is no real
 multithreading benefit to be had from const/invariant.  'Pure' is where
 it's at for that.  So maybe we're just better off without the complexities
 of const.  I've certainly gotten used to the lack of const in Python, so
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
Let me share some real world experience about using const versus not using const. Today, I found myself writing a small standalone project in D, for my own personal use. I wrote a function that took a ubtye[] as it's parameter. It occured to me that the function didn't actually modify the array, so (since I have D2.0) I could have declared the parameter const. I considered it, and then I thought: "Nah... let's not bother. Too much effort". BUT... When working on a big project, it's a different story. A couple of years back, I was team leader in a C++ project with half a dozen other developers. It was a multithreaded application. So I wrote this bunch of classes which had to be called in a certain way or it wouldn't compile - because data had to be shared between server instances and so forth. Well, a couple of the developers figured out the right thing to do all by themselves, and that was great. Of the others, about three tried the "obvious", discovered that it wouldn't compile because of const-correctness, and came to me asking why not. I explained that there was good reason for the constness, that it was thread-safety issue, and then I told them the correct, "authorised" way of doing things, and then they went off and did the right thing. So in this case, const forced everyone to get it right. Well - almost everyone. (There's always /one/!) One team member tried the "obvious", discovered that it wouldn't compile, and then, instead of asking me why not, figured out a workaround to make it compile using pointers. (If C++ had had intransitive const, the compiler would have prevented this, but it doesn't so he got away with it). Then ... weeks later ... we found that the server randomly fell over after several hours of problem-free up-time. The symptoms were random memory corruption. It took us about a month to find and fix the problem! Care to guess what the problem turned out to be? Yes, it was that errant team member's workaround for const-correctness that ended up causing multiple threads to (rarely) become thread-unsafe. (Moral: If your code won't compile when you try to call a co-worker's function, don't hack up a workaround - instead, ask them if you're calling it right!) So what I conclude from this is: (1) For a small project, you can ignore const. Just because the language has it, doesn't mean you have to use it. (2) For a large project, it's essential. And moreover, it needs to be transitive by default.
But wait... if there wasn't any const in the first place, the guy wouldn't have needed to hack up that workaround... I'm sensing a different moral here... ;-P
First, I'm not convinced that cluebie wouldn't have just casted away const to work around D's transitivity. Second, along the same lines as Robert's reply, if you created a library for which in your words "the obvious way" was not the right way to use it, and three developers had to ask you how it works, then it sounds like you may not have documented your cleverness sufficiently. Maybe if you hadn't been thinking compiler errors from constness would save you, then you would have either designed it so that it just worked "the obvious way" or at least documented it more thoroughly. Or was it documented clearly and people just ignored the documentation? That can certainly happen, too. --bb
Sep 19 2007
prev sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/19/07, Robert Fraser <fraserofthenight gmail.com> wrote:
 But wait... if there wasn't any const in the first place, the guy wouldn't
have needed to hack up that workaround...
Oh dear. I hope I'm not being misunderstood again. const forced the rest of the team (apart from this one guy) to get it right. That meant the project ended up relatively bug free. This one guy was the exception that proves the rule!
Sep 19 2007
prev sibling next sibling parent reply Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Jarrett Billingsley Wrote:

 "Bill Baxter" <dnewsgroup billbaxter.com> wrote in message 
 news:fcl79k$18vu$1 digitalmars.com...
 
 It seems from the discussion here the past week, there is no real 
 multithreading benefit to be had from const/invariant.  'Pure' is where 
 it's at for that.  So maybe we're just better off without the complexities 
 of const.  I've certainly gotten used to the lack of const in Python, so 
 why not in a C++-ish language?
I start to wonder how much of the "of COURSE we need const!" thinking comes from "if all you have is a hammer, everything starts looking like a nail." That is, I wonder how many people think we absolutely need const because they're so used to using it in C and C++. Never having learned a const-correct language (started with various BASICs, then on to very rudimentary C++, then D, and also a bit of Java and C#), I simply don't see the overwhelming need. Sure, there are a very few cases where const could be useful in my code for efficiency's sake, but I've never felt like I was missing any expressive power. Furthermore, I have been bitten exactly once by something that theoretically could have been prevented by const. It took me two minutes in the debugger to track down the problem. I'd say that those two minutes were a much better tradeoff than the god-knows-how-long it'd take to write const and non-const overloads of who-knows-how-many functions.
The problem with this is it isn't easy to tell how many problems would have been prevented if you are a regular user of const because they've been prevented. One exercise to try would be to take a large project and replace const with an empty string and see what the code looks like, then count the number, type and severity of potential errors relating to this absence. The problem is that doesn't take into account the dynamic nature of programming. Far more mistakes occur during development than you might be able to find in a finished product. An example of one that catches me out quite often is: strcpy(src,dest) vs. strcpy(dest,src); As soon as src is const and dest mustn't be this can be caught at compile time. Granted that example is academic as I use std::string in place of char*s whenever possible. So that's one use case in favour of const parameters. What happened to that "deconstructing the christmas tree" thread? Gathering the useful use cases of constness together sounds like a good way forward to me. Regards, Bruce.
Sep 17 2007
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Bruce Adams Wrote:
 The problem with this is it isn't easy to tell how many problems would have
been prevented if you are a regular user of const because they've been
prevented. One exercise to try would be to take a large project and replace
const with an empty string and see what the code looks like, then count the
number, type and severity of potential errors relating to this absence. The
problem is that doesn't take into account the dynamic nature of programming.
Far more mistakes occur during development than you might be able to find in a
finished product.
 
 
I think this is a similar argument to the one used to argue that static typing prevents a lot of bugs that dynamic typing might introduce. Many people who have worked in a dynamically-typed language (even on a bigger project) will confess that the gain in productivity of not having to constantly specify interfaces, being able to "blindly" pass things around, etc., create a better cost-to-benefit ratio than static typing. In fact, it's the same argument used to justify checked/specified exceptions in Java. The potential for a bug doesn't mean that that potential will be used, and the productivity benefits of NOT having const might be greater. That strcpy thing _is_ a good example, though it won't catch that many bugs in its usage, since src already needs to be const. But it does show a valid use-case, which is better than I can provide for my argument ;-P.
Sep 17 2007
next sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Robert Fraser Wrote:

 Bruce Adams Wrote:
 The problem with this is it isn't easy to tell how many problems would have
been prevented if you are a regular user of const because they've been
prevented. One exercise to try would be to take a large project and replace
const with an empty string and see what the code looks like, then count the
number, type and severity of potential errors relating to this absence. The
problem is that doesn't take into account the dynamic nature of programming.
Far more mistakes occur during development than you might be able to find in a
finished product.
 
 
I think this is a similar argument to the one used to argue that static typing prevents a lot of bugs that dynamic typing might introduce. Many people who have worked in a dynamically-typed language (even on a bigger project) will confess that the gain in productivity of not having to constantly specify interfaces, being able to "blindly" pass things around, etc., create a better cost-to-benefit ratio than static typing. In fact, it's the same argument used to justify checked/specified exceptions in Java.
The same argument can be applied to unit testing and contracts as well but less successfully. Dynamic / scripting languages do let you write code faster but at the expense of potentially being less reliable / robust. C++ is at the other end you over engineer and get reliable robust code - slowly. In an ideal language we could do both. "auto" almost gives us the best of both but we're currently missing an "any" type that would implicitly convert itself as freely as its dynamic language counterpart (at the expense of run-time performance).
 The potential for a bug doesn't mean that that potential will be used, and the
productivity benefits of NOT having const might be greater.
I agree the case is not proven but unlike static/dynamic typing constness is an option. You can use it or not. In theory you can add it to an implementation as sugar after knocking up a prototype without it. Though, I personally don't advocate that kind of style. I think it is very important to get constness right in the design of interfaces, under the bonnett it matters much less.
 That strcpy thing _is_ a good example, though it won't catch that many bugs in
its usage, since src already needs to be const. But it does show a valid
use-case, which is better than I can provide for my argument ;-P.
Your toadying will not go unrewarded when I am emperor ;)
Sep 17 2007
prev sibling parent 0ffh <spam frankhirsch.net> writes:
Robert Fraser wrote:
 Bruce Adams Wrote:
 The problem with this is it isn't easy to tell how many problems would
 have been prevented if you are a regular user of const because they've
 been prevented. [...]
I think this is a similar argument to the one used to argue that static typing prevents a lot of bugs that dynamic typing might introduce. [...]
As an aside, I think that people tend to use one bin for dynamic typing and the auto-instatiation of variables that many dynamically typed languages use (and which is, of course, very evil :-).
 The potential for a bug doesn't mean that that potential will be used,
 and the productivity benefits of NOT having const might be greater.
It might at least make things simpler, which is a gain in itself. The question is if this outweighs the advantages of having const. Maybe it really *is* time to deconstruct the christmas tree and have a look what kinds of constness are important enough to justify complicating the language by narrowing code freedom. Regards, frank
Sep 17 2007
prev sibling parent reply renoX <renosky free.fr> writes:
Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src) See? 1) much less risk of errors 2) better readability 3) in case of error, it's easy to see the error and correct it. So there is a better way than const to prevent source/dest error, unfortunately D doesn't support it.. Regards, RenoX
Sep 17 2007
next sibling parent reply Regan Heath <regan netmail.co.nz> writes:
renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh! Regan
Sep 17 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to? I think named/keyword parameters can be quite useful. Have you ever used a language that has them? I think the problem with putting them into D is only that it becomes yet another way to do things. We already have all the flavors of overloading inherited from C++. --bb
Sep 17 2007
next sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to? I think named/keyword parameters can be quite useful. Have you ever used a language that has them? I think the problem with putting them into D is only that it becomes yet another way to do things. We already have all the flavors of overloading inherited from C++. --bb
Actually I quite like the concept of keywords provided they're optional. I prefer the Fortran-90 syntax: copy(source=foo, dest=bar); since its sort of an assignment. Actually its just as ugly, but you can get used to almost anything eventually. That protects me from one kind of bug where I get source and dest confused. It does not protect me from getting foo and bar confused. If the source is required to be const and foo is not the const variable I am saved. i.e. myfunc( string alpha = var1, string beta = var2 ) { stuff... copy(var2,var1); } vs. myfunc( const string alpha , string beta ) { stuff... copy(source=alpha,dest = beta); } string var1 = "source" string var2; //uninitialised oh no! myfunc(alpha=var1,beta=var2); The const protects me from swapping args when the keywords hide some implementaiton detail. However, agreed positional arguments are a bad thing in general but sometimes necessary. In the above it would be useful to say - must be initialised which in C++ is: const var1 = "source"; Though, a ordinary clever compiler detect that var2 is not initialised without const. So cost benefit analysis so far. Keywords yes, maybe even before const but not a replacement. Regards, Bruce.
Sep 17 2007
prev sibling parent reply Regan Heath <regan netmail.co.nz> writes:
Bill Baxter wrote:
 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to?
Not sure, it just makes my skin crawl at the moment. I've never really had a problem with passing arguments in the wrong order. These days my IDE prompts me as I type, that feature seems to remove the need for this idea completely, to my mind. Granted not every IDE has this. It's a bit like the idea to use 'out'/'ref' 'inout' etc at call site. I can understand the argument that it increases the information available to someone simply reading the code but I just don't want to have to type all that. Again, an IDE could easily display this information for you. But again, not every IDE will have such a feature and if you decided to diplay the source in another form (printed or something) you loose the information again. I guess all I'm saying is that I'm on the other side of the fence, the gain from these additions are less than the cost, to me.
 I think
 named/keyword parameters can be quite useful.  Have you ever used a 
 language that has them?
To be fair, no, and I should probably try it before passing judgement. My last post was a 'first impression' or rather, 'another first impression' because I recall this idea being posted before, perhaps several times.
 I think the problem with putting them into D is only that it becomes yet 
 another way to do things.  We already have all the flavors of 
 overloading inherited from C++.
That true. Regan
Sep 18 2007
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Regan Heath <regan netmail.co.nz> wrote:
 Bill Baxter wrote:
 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to?
Not sure, it just makes my skin crawl at the moment.
Of course, for copying strings in D, we already have the vastly superior syntax dst = src; :-)
Sep 18 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
Janice Caron wrote:
 On 9/18/07, Regan Heath <regan netmail.co.nz> wrote:
 Bill Baxter wrote:
 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to?
Not sure, it just makes my skin crawl at the moment.
Of course, for copying strings in D, we already have the vastly superior syntax dst = src; :-)
You mean of course: dst = src.dup ;O)
Sep 18 2007
next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Regan Heath <regan netmail.co.nz> wrote:
 You mean of course:

 dst = src.dup

 ;O)
Oh yeah! Well spotted. :-)
Sep 18 2007
prev sibling parent Frits van Bommel <fvbommel REMwOVExCAPSs.nl> writes:
Regan Heath wrote:
 Janice Caron wrote:
 On 9/18/07, Regan Heath <regan netmail.co.nz> wrote:
 Bill Baxter wrote:
 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to?
Not sure, it just makes my skin crawl at the moment.
Of course, for copying strings in D, we already have the vastly superior syntax dst = src; :-)
You mean of course: dst = src.dup ;O)
Or: --- dst[] = src; --- to copy to a preallocated buffer (like strcpy does). (Or --- dst[0..src.length] = src; --- if src.length < dst.length...)
Sep 18 2007
prev sibling parent reply renoX <renosky free.fr> writes:
Regan Heath a écrit :
 Bill Baxter wrote:
 Regan Heath wrote:
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Is it the particular syntax or the concept you object to?
Not sure, it just makes my skin crawl at the moment. I've never really had a problem with passing arguments in the wrong order. These days my IDE prompts me as I type, that feature seems to remove the need for this idea completely, to my mind. Granted not every IDE has this. It's a bit like the idea to use 'out'/'ref' 'inout' etc at call site. I can understand the argument that it increases the information available to someone simply reading the code but I just don't want to have to type all that.
The IDE can write these thing for you (assuming of course you write the function prototype before the call), and for maintained code, there is more reading/rereading than writing.
 Again, an IDE could easily display this information for you.  But again, 
 not every IDE will have such a feature and if you decided to display the 
 source in another form (printed or something) you loose the information 
 again.
Yes, that's why having the IDE writing additional information for you is better: everyone benefits from it, whereas if those information are displayed only when you have an IDE, only the users with the IDE will have those information..
 I guess all I'm saying is that I'm on the other side of the fence, the 
 gain from these additions are less than the cost, to me.
As said above, developers spend more time reading code (for debugging, for modifying) than writing code.
  > I think
 named/keyword parameters can be quite useful.  Have you ever used a 
 language that has them?
To be fair, no, and I should probably try it before passing judgement. My last post was a 'first impression' or rather, 'another first impression' because I recall this idea being posted before, perhaps several times.
 I think the problem with putting them into D is only that it becomes 
 yet another way to do things.  We already have all the flavors of 
 overloading inherited from C++.
I don't follow you here, having keyword function call doesn't imply that you necessarily allow different a overloading mode.. Sure D could also use the name of the parameters as a disambiguator [which is nice IMHO sin(deg: 90) and sin(rad: pi) is better than sin_deg and sin_rad] but this is not *required*. And IMHO, it wouldn't be wise to add this parameter name overloading at first, because as you said the interactions between this an normal function calls would be weird: in the above example, you wouldn't be able to call sin(5) as the compiler wouldn't be able to distinguish which function is called. renoX
 
 That true.
 
 Regan
Sep 18 2007
parent reply Regan Heath <regan netmail.co.nz> writes:
renoX wrote:
 Regan Heath a écrit :
 I guess all I'm saying is that I'm on the other side of the fence, the 
 gain from these additions are less than the cost, to me.
As said above, developers spend more time reading code (for debugging, for modifying) than writing code.
Which they do using what application ;) The IDE, which can display the specific information each developer finds most useful and can do so without 'cluttering' the source with things another developer may not need/want. Regan
Sep 19 2007
parent reply Ingo Oeser <ioe-news rameria.de> writes:
Regan Heath wrote:

 renoX wrote:
 As said above, developers spend more time reading code (for debugging,
 for modifying) than writing code.
Which they do using what application ;) The IDE, which can display the specific information each developer finds most useful and can do so without 'cluttering' the source with things another developer may not need/want.
Really, not every developer is using some kind of IDE. A syntax highlighting editor is usually enough to feed the compiler. I'm happy that D code is nothing but text, instead of some "enriched" binary junk or junk appended to databases. Best Regards Ingo Oeser
Sep 23 2007
parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Ingo Oeser Wrote:

 Regan Heath wrote:
 
 renoX wrote:
 As said above, developers spend more time reading code (for debugging,
 for modifying) than writing code.
Which they do using what application ;) The IDE, which can display the specific information each developer finds most useful and can do so without 'cluttering' the source with things another developer may not need/want.
Really, not every developer is using some kind of IDE. A syntax highlighting editor is usually enough to feed the compiler. I'm happy that D code is nothing but text, instead of some "enriched" binary junk or junk appended to databases. Best Regards Ingo Oeser
But should language features be added to maker reading code easier?
Sep 23 2007
next sibling parent Derek Parnell <derek psych.ward> writes:
On Sun, 23 Sep 2007 17:58:34 -0400, Robert Fraser wrote:


 But should language features be added to maker reading code easier?
YES YES YES! (imnsho) Code is examined via more than one method. An IDE is not the only way to look at code. There is email, B&W printers, hand writting, plain text editors, voice (over the telephone), telnet for Bob's sake, etc ... -- Derek Parnell Melbourne, Australia skype: derek.j.parnell
Sep 23 2007
prev sibling parent Ingo Oeser <ioe-news rameria.de> writes:
Robert Fraser wrote:

 But should language features be added to maker reading code easier?
Yes. Many projects use domain specific languages to make the code more readable. Very common are table generators. The Linux kernel project uses several enhancements to support checkers while improving readability. One example is endian annotation. You often get data from the network or through some hardware in a specific endianess. You only have to convert that when you start calculating with that stuff or try to hand it out to user space with an API which is defined in native byte order. Since in some networking situations most received stuff can be dropped (e.g. firewall in extremely unfriendly environment), it is wise to defer endian conversion to latest possible. To get all the situations right where we handle native or network byte order, the structure members and function arguments are endian annotated. Now you just need to look at the function signature to get it right. Or run a static checker (like sparse) to check correctness. But really, that stuff is like medicine: The dose matters :-) Best Regards Ingo Oeser
Sep 24 2007
prev sibling parent reply Derek Parnell <derek nomail.afraid.org> writes:
On Mon, 17 Sep 2007 22:57:05 +0100, Regan Heath wrote:

 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Or ... copy(from: var_dest, to: my_src); -- Derek (skype: derek.j.parnell) Melbourne, Australia 18/09/2007 9:50:52 AM
Sep 17 2007
parent Derek Parnell <derek nomail.afraid.org> writes:
On Tue, 18 Sep 2007 09:52:46 +1000, Derek Parnell wrote:

 On Mon, 17 Sep 2007 22:57:05 +0100, Regan Heath wrote:
 
 renoX wrote:
 Bruce Adams a écrit :
 An example of one that catches me out quite often is:
 strcpy(src,dest) vs. strcpy(dest,src);
If it helps you'not the only one to have this kind of issue: some compilers even try to detect when people makes mistakes for 'memset'? Both case show why function calls with passing the parameter by position instead of by keywords *sucks*. Let's try it: do you think you would make the same mistake if you would call your function this way: char[50] var_dest, my_src; strcpy(dest: var_dest, src: my_src)
My eyes... aaarghh!
Or ... copy(from: var_dest, to: my_src);
P.S. This still doesn't remove the usefulness of 'const' concept though. For example .. copy(from: ExampleA, to: ExampleB); This line by itself doesn't tell the reader if 'ExampleB' is writeable or not. -- Derek (skype: derek.j.parnell) Melbourne, Australia 18/09/2007 11:04:23 AM
Sep 17 2007
prev sibling parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"renoX" <renosky free.fr> wrote in message 
news:fcmsmv$1drt$1 digitalmars.com...

 strcpy(dest: var_dest, src: my_src)
My eyes... ahhhh! They feel so good having seen such self-explanatory code. Code that reads close to natural language == win, as far as I'm concerned.
Sep 17 2007
prev sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Lionello Lunesu Wrote:

 void bar() throw;
 void foo() { bar(); }//ERROR, bar can throw
 void foo2() throw { bar(); } //OK
 void foo3() throw { bar() throw; } //OK ("cast")
In other words, all exceptions are checked exceptions...? This would especially be a problem since all allocations can throw exceptions unless caught. This sounds like it would introduce MORE problems than Java's checked exceptions. I think the "safe" case sis typically the marked one (i.e. Walter's "nothrow" he mentioned at the conference, const, pure, etc.). The whole red code-green code idea isn't bad, but I think that should generally be relegated to documentation instead of making the compiler check it. There seems to be a general push (among many computer scientists) to enforce stricter rules, yet some of the most successful languages in the past few years have been dynamically/duck typed. The price is that the impetus of using the code correctly falls on the programmer and not the compiler/interpreter. The reward is much greater productivity, since the programmer doesn't need to be concerned about such things as const-correctness, checked exceptions or interface specification, she just works with the knowledge the compiler trusts her enough to do the right thing. I'm not suggesting D go dynamically-typed (doesn't work so well in a compiled language), but restrictions for the sake of restrictions should be looked upon with great care. If const or pure can help optimization and make paralell programming easier, I'm all for it, but if it'll just sit there and make my life harder then it's not worth it. I'd like to pose a question to those who have used C++'s const: do you feel that it has saved more time by preventing bugs than it has taken by being forced to type it all the time, and the time spent when it has to be removed all throughout a hierarchy, as inevitably has to happen at least once? That is, const-correctness is a time investment, so do you feel that investment has paid off for you? I can say that working in Java, I have _never_ felt that if I pass a class reference that was "constant" in nature to a method written by somebody else or even to entirely different subsystem, that the invariantness contract, specified only in the documentation, would be broken. Compiler checks in that case end up being as useless and annoying as checked exceptions.
Sep 17 2007
next sibling parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"Robert Fraser" <fraserofthenight gmail.com> wrote in message 
news:fcmngv$qhq$1 digitalmars.com...

 I'm not suggesting D go dynamically-typed (doesn't work so well in a 
 compiled language),
Bit of a sidetrack here. Dynamic typing, in the sense that variables don't have types, certainly doesn't go well in a compiled language, but type inference, something that ends up looking very similar to dynamic typing, _does_, can, and has been implemented well in compiled languages. ML and Haskell are examples. Nemerle as well, because even though it compiles to a VM it's still a statically typed VM. It's almost like IFTI or variable declaration type inference (auto x = 5), but extended to virtually _everything_. One language I've seen that I really liked was Bla. It uses Haskell-style type inference, but it still allows you to explicitly type things if you want. In this way you can do away with typing variables/params in a vast majority of the cases, and in the instances when you _want_ something to be typed, or when the type inference system can't figure it out on its own, you can type it manually. Of course something like this would probably be far too much of a departure for a language like D.
Sep 17 2007
parent Jari-Matti =?ISO-8859-1?Q?M=E4kel=E4?= <jmjmak utu.fi.invalid> writes:
Jarrett Billingsley wrote:

 One language I've seen that I really liked was Bla.  It uses Haskell-style
 type inference, but it still allows you to explicitly type things if you
 want.  In this way you can do away with typing variables/params in a vast
 majority of the cases, and in the instances when you _want_ something to
 be typed, or when the type inference system can't figure it out on its
 own, you can type it manually.
 
 Of course something like this would probably be far too much of a
 departure for a language like D.
Of course type systems aren't one dimensional - there can be several kinds of implicit static typing too. I had an impression that the lack of type inference in some places is only temporary. I agree that some advanced type techniques involving a Turing complete compile time language are not exactly what the users are expecting. Heh, they might be in the future, but let's not tell anyone - D still has reputation being a "practical language" for "real world tasks".
Sep 17 2007
prev sibling next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Robert Fraser wrote:
 Lionello Lunesu Wrote:
 
 There seems to be a general push (among many computer scientists) to
 enforce stricter rules, yet some of the most successful languages in
 the past few years have been dynamically/duck typed. 
Note however that as these languages mature people are gradually trying to put some notion of interface-checking back in. I only really know about python, but there we have pyprotocols (http://peak.telecommunity.com/PyProtocols.html) and zope.interface (http://www.zope.org/Products/ZopeInterface) that both aim to put some non-duck type checking features back into the language. Because, surprise, when you're scaling up to huge systems it becomes difficult to figure out exactly what kind of duck you're supposed to be passing.
 I'd like to pose a question to those who have used C++'s const: do
 you feel that it has saved more time by preventing bugs than it has
 taken by being forced to type it all the time, and the time spent
 when it has to be removed all throughout a hierarchy, as inevitably
 has to happen at least once? That is, const-correctness is a time
 investment, so do you feel that investment has paid off for you?
That's kind of why I started this thread. I started using C++ in 1995 and it has been my main language since about 1998. I'm very used to C++'s const. But I do vaguely recall finding it terribly annoying in 1995 when I started moving over from C. The C++ people kept telling me that const correctness was like eating your peas. "You may not like it, but it's good for you." And now I like const, just like I like eating peas now too. It's not that I prefer the taste of peas to chocolate ice cream, but if I don't eat my peas I get this feeling like my health may fall apart at any minute. It's the same feeling I get from not using const. That said, I can program in Python without const and not flinch at all. Because const correctness is just not a part of Python. There are no peas in Python-land so I don't feel like I have to eat them. It's ice cream for every meal! Of course in Python-land "slow" has also been declared the new "fast", so I also don't flinch about making heap allocations willy-nilly.
 I can say that working in Java, I have _never_ felt that if I pass a
 class reference that was "constant" in nature to a method written by
 somebody else or even to entirely different subsystem, that the
 invariantness contract, specified only in the documentation, would be
 broken. Compiler checks in that case end up being as useless and
 annoying as checked exceptions.
Here's the one case that makes me want const: Efficient vector math. Say you're writing a routine to compute whether or not three points are inside the circumcircle defined by three others. Here's how I wrote it in D: // Return if point d is in the circumcircle defined by points a,b,c. // The points a,b,c should be given in counter-clockwise order. bool in_circle(Point a, Point b, Point c, ref Point d) { a -= d; b -= d; c -= d; assert(is_CCW(a,b,c), "input circle points are not in ccw order"); Scalar a2 = a.sqrnorm(); Scalar b2 = b.sqrnorm(); Scalar c2 = c.sqrnorm(); Scalar det = a2*(b.x*c.y - b.y*c.x) + b2*(a.y*c.x - a.x*c.y) + c2*(a.x*b.y - a.y*b.x); return det >= 0; } Really I would like to make 'ref Point d' there be const. I'm passing it by reference because it's slightly more efficient and in_circle can get called a *lot*. I go ahead and pass a,b,c by value because I'm going to have to push a copy on the stack to modify them anyway. If I weren't modifying them I'd pass them by reference, too. But those are implementation details that the caller of in_circle doesn't really care about. So it's odd they should be in the interface. Ref/const ref is not a very direct solution for this kind of need. What i'd really like to be able to do is declare the function to be generically non-mutating (like 'in'), but at the same time time a) allow the implementation to modify its arguments if it wants to and b) make the call using the most efficient mechanism possible. I don't really want to have to guess whether the argument is big enough to justify pass by reference or not. It probably depends a lot on the architecture and number of accesses to the variable actually made in the end. Just using plain 'ref' would maybe be an acceptable solution, except you can't pass literals by ref. So if you make a min template like "T min(T)(ref T a, ref T b) {...};" min(0,x) won't compile. That's just too useful to disallow. And then if T is a class type then it's already a reference so there's not need to take a reference to the reference. You can work around these things with lots of static if(is(T==class)) type things, but it gets ugly fast. It would be great if something like T min(T)(in T a, in T b) {...} "just worked". I.e. prevented visible modifications to a,b but didn't do unnecessary, inefficient copying of big structures, and didn't take unnecessary references of arguments that are already references, and allowed calling with literals. Give me a way to do that and I'll be happy. I'd even be willing to ditch the 'prevent modifications' bit as long as I can have efficiency and the ability to pass constants. --bb
Sep 17 2007
next sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

 Robert Fraser wrote:
 Lionello Lunesu Wrote:
 
 There seems to be a general push (among many computer scientists) to
 enforce stricter rules, yet some of the most successful languages in
 the past few years have been dynamically/duck typed. 
Note however that as these languages mature people are gradually trying to put some notion of interface-checking back in. I only really know about python, but there we have pyprotocols (http://peak.telecommunity.com/PyProtocols.html) and zope.interface (http://www.zope.org/Products/ZopeInterface) that both aim to put some non-duck type checking features back into the language. Because, surprise, when you're scaling up to huge systems it becomes difficult to figure out exactly what kind of duck you're supposed to be passing.
So in other words the duck stops here ;)
Sep 18 2007
prev sibling parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

 Robert Fraser wrote:
 Lionello Lunesu Wrote:
 
 There seems to be a general push (among many computer scientists) to
 enforce stricter rules, yet some of the most successful languages in
 the past few years have been dynamically/duck typed. 
Note however that as these languages mature people are gradually trying to put some notion of interface-checking back in. I only really know about python, but there we have pyprotocols (http://peak.telecommunity.com/PyProtocols.html) and zope.interface (http://www.zope.org/Products/ZopeInterface) that both aim to put some non-duck type checking features back into the language. Because, surprise, when you're scaling up to huge systems it becomes difficult to figure out exactly what kind of duck you're supposed to be passing.
 I'd like to pose a question to those who have used C++'s const: do
 you feel that it has saved more time by preventing bugs than it has
 taken by being forced to type it all the time, and the time spent
 when it has to be removed all throughout a hierarchy, as inevitably
 has to happen at least once? That is, const-correctness is a time
 investment, so do you feel that investment has paid off for you?
That's kind of why I started this thread. I started using C++ in 1995 and it has been my main language since about 1998. I'm very used to C++'s const. But I do vaguely recall finding it terribly annoying in 1995 when I started moving over from C. The C++ people kept telling me that const correctness was like eating your peas. "You may not like it, but it's good for you." And now I like const, just like I like eating peas now too. It's not that I prefer the taste of peas to chocolate ice cream, but if I don't eat my peas I get this feeling like my health may fall apart at any minute. It's the same feeling I get from not using const. That said, I can program in Python without const and not flinch at all. Because const correctness is just not a part of Python. There are no peas in Python-land so I don't feel like I have to eat them. It's ice cream for every meal! Of course in Python-land "slow" has also been declared the new "fast", so I also don't flinch about making heap allocations willy-nilly.
 I can say that working in Java, I have _never_ felt that if I pass a
 class reference that was "constant" in nature to a method written by
 somebody else or even to entirely different subsystem, that the
 invariantness contract, specified only in the documentation, would be
 broken. Compiler checks in that case end up being as useless and
 annoying as checked exceptions.
Here's the one case that makes me want const: Efficient vector math. Say you're writing a routine to compute whether or not three points are inside the circumcircle defined by three others. Here's how I wrote it in D: // Return if point d is in the circumcircle defined by points a,b,c. // The points a,b,c should be given in counter-clockwise order. bool in_circle(Point a, Point b, Point c, ref Point d) { a -= d; b -= d; c -= d; assert(is_CCW(a,b,c), "input circle points are not in ccw order"); Scalar a2 = a.sqrnorm(); Scalar b2 = b.sqrnorm(); Scalar c2 = c.sqrnorm(); Scalar det = a2*(b.x*c.y - b.y*c.x) + b2*(a.y*c.x - a.x*c.y) + c2*(a.x*b.y - a.y*b.x); return det >= 0; } Really I would like to make 'ref Point d' there be const. I'm passing it by reference because it's slightly more efficient and in_circle can get called a *lot*. I go ahead and pass a,b,c by value because I'm going to have to push a copy on the stack to modify them anyway. If I weren't modifying them I'd pass them by reference, too. But those are implementation details that the caller of in_circle doesn't really care about. So it's odd they should be in the interface. Ref/const ref is not a very direct solution for this kind of need. What i'd really like to be able to do is declare the function to be generically non-mutating (like 'in'), but at the same time time a) allow the implementation to modify its arguments if it wants to and b) make the call using the most efficient mechanism possible. I don't really want to have to guess whether the argument is big enough to justify pass by reference or not. It probably depends a lot on the architecture and number of accesses to the variable actually made in the end. Just using plain 'ref' would maybe be an acceptable solution, except you can't pass literals by ref. So if you make a min template like "T min(T)(ref T a, ref T b) {...};" min(0,x) won't compile. That's just too useful to disallow. And then if T is a class type then it's already a reference so there's not need to take a reference to the reference. You can work around these things with lots of static if(is(T==class)) type things, but it gets ugly fast. It would be great if something like T min(T)(in T a, in T b) {...} "just worked". I.e. prevented visible modifications to a,b but didn't do unnecessary, inefficient copying of big structures, and didn't take unnecessary references of arguments that are already references, and allowed calling with literals. Give me a way to do that and I'll be happy. I'd even be willing to ditch the 'prevent modifications' bit as long as I can have efficiency and the ability to pass constants. --bb
Showing my true colours as a D noob here. I thought that with D like in java any object argument is passed by reference by default but unlike java the D compiler has the option of passing by value if it decides it would be more efficient. A const declaration here would help that. I believe it should be down to the compiler to decide when to do this because it makes no visible difference from the programmers perspective. constness is part of the contract but perhaps allowing copy on write where you want to modify arguments is an option. It may be a dangerous one because it allows you to shoot yourself in the foot and modify a variable thinking you are modifying the real one when actually you are only modifying a local copy. The inverse of the normal problem. Bruce.
Sep 18 2007
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Robert Fraser wrote:
 I'd like to pose a question to those who have used C++'s const: do
 you feel that it has saved more time by preventing bugs than it has
 taken by being forced to type it all the time, and the time spent
 when it has to be removed all throughout a hierarchy, as inevitably
 has to happen at least once? That is, const-correctness is a time
 investment, so do you feel that investment has paid off for you?
At the upcoming http://www.astoriaseminar.com, I think I'll do some asking around on this issue. There'll be a lot of C++ diehards there.
 I can say that working in Java, I have _never_ felt that if I pass a
 class reference that was "constant" in nature to a method written by
 somebody else or even to entirely different subsystem, that the
 invariantness contract, specified only in the documentation, would be
 broken.
Some Java professionals have reported problems with not being able to specify const classes. These people work on Java programs with large teams.
 Compiler checks in that case end up being as useless and
 annoying as checked exceptions.
I don't think that's the same issue. The problem with checked exceptions is that suppose you have functions A, B, C, where A calls B, and B calls C. Now, you throw a new exception in C, and catch it in A. Arggh, you've got to change B.
Sep 17 2007
next sibling parent reply Robert Fraser <fraserofthenight gmail.com> writes:
Walter Bright Wrote:

 Robert Fraser wrote:
 I'd like to pose a question to those who have used C++'s const: do
 you feel that it has saved more time by preventing bugs than it has
 taken by being forced to type it all the time, and the time spent
 when it has to be removed all throughout a hierarchy, as inevitably
 has to happen at least once? That is, const-correctness is a time
 investment, so do you feel that investment has paid off for you?
At the upcoming http://www.astoriaseminar.com, I think I'll do some asking around on this issue. There'll be a lot of C++ diehards there.
 I can say that working in Java, I have _never_ felt that if I pass a
 class reference that was "constant" in nature to a method written by
 somebody else or even to entirely different subsystem, that the
 invariantness contract, specified only in the documentation, would be
 broken.
Some Java professionals have reported problems with not being able to specify const classes. These people work on Java programs with large teams.
I work (well, intern...) on a Java project with a team of ~35 developers. Can't say I've ever really wanted to constify something, although needing to synchronize everything when in doubt does get annoying (and as I keep telling my boss hurts performance dramatically, but they're big fans of the "throw more hardware at it" form of performance refactoring) so knowing something is INVARIANT might occasionally be helpful. But as a purely interface thing, I think docs serve better.
 
 Compiler checks in that case end up being as useless and
 annoying as checked exceptions.
I don't think that's the same issue. The problem with checked exceptions is that suppose you have functions A, B, C, where A calls B, and B calls C. Now, you throw a new exception in C, and catch it in A. Arggh, you've got to change B.
Same with const, but top-down. Make something const in A, you have to make it const in B and C. If you ague that if B and C weren't changing it, then what if C's implementation changed in such a way as it dos make a change. Now, arrgh, got to go back and change it in B and A. It's not nearly as bad as checked exceptions, but const for const's sake is excessive and useless. Hopefully, though, IDEs will be able to do it all for you (Descent will get there someday!).
Sep 17 2007
parent Walter Bright <newshound1 digitalmars.com> writes:
Robert Fraser wrote:
 But as a purely interface thing, I
 think docs serve better.
The problem with docs is they are invariably wrong, out of date, ambiguous, or missing. Docs are also unreadable by analysis tools, and can't be relied upon by code auditors.
 Same with const, but top-down. Make something const in A, you have to
 make it const in B and C.
Only if B and C were done poorly to begin with, and didn't already say they didn't change the reference.
 If you ague that if B and C weren't
 changing it, then what if C's implementation changed in such a way as
 it dos make a change. Now, arrgh, got to go back and change it in B
 and A.
But that is a *relevant* change. The exception passthrough is not relevant, as it means nothing to B.
Sep 18 2007
prev sibling next sibling parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Walter Bright <newshound1 digitalmars.com> wrote:
 Robert Fraser wrote:
 I'd like to pose a question to those who have used C++'s const: do
 you feel that it has saved more time by preventing bugs than it has
 taken by being forced to type it all the time, and the time spent
 when it has to be removed all throughout a hierarchy, as inevitably
 has to happen at least once? That is, const-correctness is a time
 investment, so do you feel that investment has paid off for you?
At the upcoming http://www.astoriaseminar.com, I think I'll do some asking around on this issue. There'll be a lot of C++ diehards there.
The issues in C++ aren't necessarily the same as the issues in D, however. Perhaps the most significant thing is that in C++, all classes are passed by value by default. That means that function parameters are essentially const by default, because the function gets its own copy of the object rather than referencing the original. To pass by pointer or reference, you must explicitly code it to do that, and only then does const start to creep in. This can lead to some very awkward coding practices, e.g. void f(std::string const & s); ...which basically does exactly the same job as void f(std::string s) Obviously you don't need "const" in the latter case because you're passing by value, but as soon as you start passing by reference (for efficiency - it avoids unnecessary copy constructor and destructor calls) you suddenly start to need const. In D, things are a bit different, because (as in Java) classes are passed by reference, and that means that the const keyword is going to be needed a lot more. An alternative approach might be to have reference types passed to functions as const by default, requiring the function author to explicitly state (by means of the ref keyword) that the object is mutable. This would mean that classes would then have exactly the same semantics as structs. e.g. struct S; class C; void f(S s); /* f gets a copy of s, so cannot modify the original */ void f(ref S s); /* s passed by reference, so f can modify it */ void f(C c); /* s passed by const reference, so f cannot modify it */ void f(ref C c); /* s passed by reference-to-mutable, so f can modify it */ Of course, this brings us back to the head/tail const distinction. In the above examples, we are only concerned with the constness of the object's members. In the fourth example, we arrive at a situation which /can never happen in C++/, because in C++, references are /always/ head-const. That is: void f(C & c) { s = new C(); /*ERROR - c is a reference */ } will not compile, because even though c was not declared as const, it is nonetheless head-const /because it is a reference/ This leads me to my second thought (I have more...), which is the notion that all function parameters should be head-const, not just by default, but absolutely. This would support all of the preceeding argument, but it would also mean that, for example void f(int n) { ++n; /* Error */ } would no longer compile. That's not the end of the world, because n is local anyway. The only change the programmer would need make to their code is to make a local copy of n, like this: void f(int n0) { n = n0; /* local copy - may modify */ ++n; /* OK */ } which brings me to my third and final observation, which is that this scheme needs one final "fix" before it becomes usable, because, as I've described it above, structs would be passed by value, and yet the function would not be able to modify them. And obviously that's bad. So here's the final trick to make it all hunky dory. For value types, such as structs or ints, we (that is, the compiler), divide them into two categories: Category A consists of all primitive types, and all structs which are less than some threshold size (say, 16 bytes). Category B consists of all remaining structs. In summary, category = (T.sizeof < 16) ? "A" : "B". Category A objects are passed by value. Category B objects are passed by reference. Some examples would help explain: --------------------------- struct SmallStruct { int x; int y; } SmallStruct s; f(s); void f(SmallStruct s) /* s is passed by value and is head-const */ { s.x = 3; /* Error - s is head-const */ SmallStruct s2 = s; s2.x = 3; /* OK */ } void g(ref BigStruct s) /* s is passed by reference and is head-const */ { s.x = 3; /* OK */ } --------------------------- struct BigStruct { int x; int[100] y; } SmallStruct s; f(s); void f(BigStruct s) /* s is passed by reference and is head-const and tail-const */ { s.x = 3; /* Error - s is tail-const */ SmallStruct s2 = s; s2.x = 3; /* OK */ } void g(ref BigStruct s) /* s is passed by reference and is head-const */ { s.x = 3; /* OK */ } --------------------------- class MyClass { int x; int y; } MyClass s; f(s); void f(MyClass s) /* s is already a reference, which passed by copy, but we consider it head-const and tail-const */ { s = new MyClass; /* Error - s is head-const */ s.x = 3; /* Error - s is tail-const */ MyClass s2 = s.dup; s2.x = 3; /* OK */ } void g(ref MyClass s) /* s is already a reference, which passed by copy, but we consider it head-const */ { s = new MyClass; /* Error - s is head-const */ s.x = 3; /* OK */ } --------------------------- The important thing to observe in these examples is that everything works the same - the semantics are identical at both the caller site and the callee site. The second important thing to observe is that the word "const" is completely absent from these examples. If you have const-by-default, you don't need it. Instead, you work around head-constness by making a local copy, and you override tail-constness by using the "ref" keyword. Plus - you get built-in effeciency for passing large structs to functions. I believe that this will help programmers to write code quickly without having to remember to write "const" all over the place. If they need to modify the original, the compiler will remind them to throw in a "ref" keyword to make it explicit. Everyone wins. It's easy to write code, and the compiler gets to do its checking.
Sep 18 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
About passing structs by reference: It's one of those things, like
register or inline...

Once upon a time, programmers used the keyword "register" because they
thought they could do a better job than the compiler at figuring how
to use its registers.

Once upon a time, programmers used the keyword "inline" because they
thought they could do a better job than the compiler at figuring out
what to inline and what not.

Now people explicitly choose between f(s) and f(ref s) (where s is a
struct and the function does not modify it) because they think they
can do a better job than the compiler at figuring out how to pass
parameters to functions. I say give control back to the compiler. Lets
let "ref" mean "the function may modify the parameter", and the
absense of ref mean "the function may not modify the parameter", but
leave it to the compiler to decide what is the most efficient way to
pass the data to the function.

I think this is a neat idea, however it won't work unless the compiler
can also do const-checking. This is one more optimisation which having
const allows. (And const-by-default would make it obvious).

----

I have thought of a problem though. Under the scheme I outlined in a
previous post, it would no longer be possible for a function to modify
the original reference. That is:

class C;
C c;
f(c)

void f(ref C c)
{
    c = new C(); /* compile-time error */
}

would no longer compile, because under the new scheme, "ref" is
reinterpreted to mean that c is head-const (because C is a class - see
definitions in previous post). So instead, you'd have to do:

class C;
C c;
f(&c)

void f(ref C* c)
{
    *c = new C();
}

Whether or not that would be considered a prohibiting problem, I don't know.
Sep 18 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...
 
 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.
 
 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.
 
 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function. So for instance "pass by ref unless something in the function modifies the value" is not going to work. But something like "pass by value unless the size of the parameter is greater than 8 bytes" could work. I think this is a separate issue from explicitly passing things as 'ref'. The above seems more appropriate to me as an implementation of 'in' parameters. For compiling function bodies, if the compiler detects a modification of an 'in' parameter that it chose to implement using pass-by-reference then it would just generate code in the function body to copy the parameter value first first thing. E.g. void foo(in BigStruct x); compiler actually generates code like: void foo(ref BigStruc x) { ... } unless the body of foo modifies x. Then the code gen changes to: void foo(ref BigStruct x_) { BigStruct x = x_; ... } There would still be a bit of a compiler compatibility issue though. For a given platform Compiler vendors A and B would need to agree on the rules for 'in' parameters or the libs they generate would be incompatible. If we want D to be a language with a unified ABI at least. That suggests that the rules would need to be part of the spec. Heck it may be good enough to just pick a max parameter size and stick with it for all platforms. Just like D picks a size for 'float' and sticks with it for all platforms, whether it's the most efficient for that platform or not. --bb
Sep 18 2007
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 This thought has occurred to me before to.  I think issue becomes that
 the function signature may no longer enough for a compiler to tell what
 kind of code it needs to generate to call the function.
That is also true for inline. I believe the solution for the pass-by-ref vs pass-by-value decision is that the compiler generates both versions of the function, and then may throw one of them away at link-time.
Sep 18 2007
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 This thought has occurred to me before to.  I think issue becomes that
 the function signature may no longer enough for a compiler to tell what
 kind of code it needs to generate to call the function.
That is also true for inline. I believe the solution for the pass-by-ref vs pass-by-value decision is that the compiler generates both versions of the function, and then may throw one of them away at link-time.
So the compiler generates 2^N versions for an N-parameter method? --bb
Sep 18 2007
next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 So the compiler generates 2^N versions for an N-parameter method?
Still only two ... I /think/ The first version is the version in which all information is known. This is the version in which optimal decisions are assumed at the callee site, and may be taken at the caller site. The second version is the version in which no information is known. In this version, everything gets passed by the default behaviour. So, at the callee site, either all information about the function is known, in which case we call version A, or it isn't in which case we call version B. I think that would work. It may not be /quite/ as efficient as the 2^N possibility you mentioned, but it would certainly be more efficient than the status quo (version B for everything).
Sep 18 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 So the compiler generates 2^N versions for an N-parameter method?
Still only two ... I /think/ The first version is the version in which all information is known. This is the version in which optimal decisions are assumed at the callee site, and may be taken at the caller site. The second version is the version in which no information is known. In this version, everything gets passed by the default behaviour.
That makes sense and seems like it could work if you have const refs to use as the fallback. Otherwise the fallback is going to be pass-by-value, which would be terrible for any BigStruct parameters. Object files would unfortunately just about double in size overnight... --bb
Sep 18 2007
prev sibling parent Ingo Oeser <ioe-news rameria.de> writes:
Bill Baxter wrote:

 Janice Caron wrote:
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 This thought has occurred to me before to.  I think issue becomes that
 the function signature may no longer enough for a compiler to tell what
 kind of code it needs to generate to call the function.
That is also true for inline. I believe the solution for the pass-by-ref vs pass-by-value decision is that the compiler generates both versions of the function, and then may throw one of them away at link-time.
So the compiler generates 2^N versions for an N-parameter method?
No, just for the cases, where it really matters in code size and/or speed. Isn't that kind of optimisation called "function versioning" in compiler folk slang? Once you compile in project global scope, you can do a lot of fun stuff. The problem is: We usually don't compile code that way :-( Best Regards Ingo Oeser
Sep 18 2007
prev sibling next sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
 So for instance
 "pass by ref unless something in the function modifies the value" is not
 going to work.
Agreed. But that's not what I suggested. You'd probably have to head back to see the earlier post to see the suggestion in full, because it's too long to repost, but basically it all works (...or at least, I think so...) providing you have parameters passed as const by default, which is an argument in favor of needing const. (If this discussion gets too complicated, we can take it to another thread)
Sep 18 2007
next sibling parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Janice Caron wrote:
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
 So for instance
 "pass by ref unless something in the function modifies the value" is not
 going to work.
Agreed. But that's not what I suggested. You'd probably have to head back to see the earlier post to see the suggestion in full, because it's too long to repost, but basically it all works (...or at least, I think so...) providing you have parameters passed as const by default, which is an argument in favor of needing const. (If this discussion gets too complicated, we can take it to another thread)
Yeh, sorry my response was more along the lines of "that's an interesting idea that reminds me of this idea *I* had". Your idea requires const by default, but unfortunately const by default has been beaten to death around here. Nearly everyone who cares about D lobbied for giving it a try as Walter was designing const for D2.0, but in the end Walter said, no, it's too big a departure from C++. --bb
Sep 18 2007
prev sibling parent reply Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Janice Caron Wrote:

 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
S could be an opaque type so s.sizeof may still be undefined. Bruce.
Sep 18 2007
next sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Bruce Adams wrote:
 Janice Caron Wrote:
 
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
 S could be an opaque type so s.sizeof may still be undefined.
You can't declare a function that takes an argument of unknown size. You just can't. The compiler will complain that it doesn't know the type. So either your "opaque type" is not allowable, or it's actually a reference/pointer to "opaque type" in which case the size of the pointer *is* known, which is all that is needed, since that's all that will be passed to the function. --bb
Sep 18 2007
parent reply Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

 Bruce Adams wrote:
 Janice Caron Wrote:
 
 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
 S could be an opaque type so s.sizeof may still be undefined.
You can't declare a function that takes an argument of unknown size. You just can't. The compiler will complain that it doesn't know the type. So either your "opaque type" is not allowable, or it's actually a reference/pointer to "opaque type" in which case the size of the pointer *is* known, which is all that is needed, since that's all that will be passed to the function. --bb
See my reply to Janice on channel B. I did mean its passed by reference. I don't quite get the structs are passed by value by default thing. I'm assuming everything is passed by reference by default but that sometimes you want the compiler to pass by value as for small data types by they classes or structs its more efficient. So my point is that for an opaque type you may not have access to sizeof. An opaque type is like a class or struct whose data members are all private but done properly. You don't need to know about the implementation. C++ forces you to expose the implementation by declaring the members in the header even though they're private and client code can't do anything with them. The caveat is you do still need to know the size to reserve space for an opaque type before its constructed. This is why a C++ compiler needs a full definition. In theory it could be sorted at link time with support from the object file format or even left until run-time. My understanding was that you can do opaque types properly in D though I haven't tried myself. So you have sizeof at link time but not necessarily at compile time. Regards, Bruce.
Sep 18 2007
parent Reiner Pope <some address.com> writes:
Bruce Adams wrote:
 Bill Baxter Wrote:
 
 Bruce Adams wrote:
 Janice Caron Wrote:

 On 9/18/07, Bill Baxter <dnewsgroup billbaxter.com> wrote:
 Janice Caron wrote:
 About passing structs by reference: It's one of those things, like
 register or inline...

 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.

 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.

 Now people explicitly choose between f(s) and f(ref s) (where s is a
 struct and the function does not modify it) because they think they
 can do a better job than the compiler at figuring out how to pass
 parameters to functions. I say give control back to the compiler. Lets
 let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
This thought has occurred to me before to. I think issue becomes that the function signature may no longer enough for a compiler to tell what kind of code it needs to generate to call the function.
Actually, I think it's possible my proposal may not have been understood. Under my suggestion, if the caller passes a struct... f(s) ...and the callee declares the function as... void f(S s) ...then all information is known. Both ends must surely know s.sizeof at compile time? And since that's the /only/ thing the compiler needs to know to make the decision.
S could be an opaque type so s.sizeof may still be undefined.
You can't declare a function that takes an argument of unknown size. You just can't. The compiler will complain that it doesn't know the type. So either your "opaque type" is not allowable, or it's actually a reference/pointer to "opaque type" in which case the size of the pointer *is* known, which is all that is needed, since that's all that will be passed to the function. --bb
See my reply to Janice on channel B. I did mean its passed by reference. I don't quite get the structs are passed by value by default thing. I'm assuming everything is passed by reference by default but that sometimes you want the compiler to pass by value as for small data types by they classes or structs its more efficient. So my point is that for an opaque type you may not have access to sizeof. An opaque type is like a class or struct whose data members are all private but done properly. You don't need to know about the implementation. C++ forces you to expose the implementation by declaring the members in the header even though they're private and client code can't do anything with them. The caveat is you do still need to know the size to reserve space for an opaque type before its constructed. This is why a C++ compiler needs a full definition. In theory it could be sorted at link time with support from the object file format or even left until run-time. My understanding was that you can do opaque types properly in D though I haven't tried myself. So you have sizeof at link time but not necessarily at compile time. Regards, Bruce.
These opaque types sound suspiciously like D classes/interfaces. void foo(Object o) { assert(o.sizeof == (void*).sizeof); } But C++ polymorphism also allows that, no? But, as other people have pointed out, doing this with structs is a completely different story. -- Reiner
Sep 18 2007
prev sibling parent reply "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Under my suggestion, if the caller passes a struct...

 f(s)

 ...and the callee declares the function as...

 void f(S s)

 ...then all information is known. Both ends must surely know s.sizeof
 at compile time? And since that's the /only/ thing the compiler needs
 to know to make the decision.
S could be an opaque type so s.sizeof may still be undefined.
Could you explain further? I don't understand what an "opaque type" is in D. Observe that the example I cited above explicitly states that s be a struct, not a class. Given that the struct would ordinarily have to be built on the stack at the caller site, and copied onto the stack at the callee site, I just don't see how s.sizeof can be unknown at the time the function is instantiated, /even if/ was declared using a template. What am I missing?
Sep 18 2007
parent reply Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Janice Caron Wrote:

 On 9/18/07, Bruce Adams <tortoise_74 yeah.who.co.uk> wrote:
 Under my suggestion, if the caller passes a struct...

 f(s)

 ...and the callee declares the function as...

 void f(S s)

 ...then all information is known. Both ends must surely know s.sizeof
 at compile time? And since that's the /only/ thing the compiler needs
 to know to make the decision.
S could be an opaque type so s.sizeof may still be undefined.
Could you explain further? I don't understand what an "opaque type" is in D. Observe that the example I cited above explicitly states that s be a struct, not a class. Given that the struct would ordinarily have to be built on the stack at the caller site, and copied onto the stack at the callee site, I just don't see how s.sizeof can be unknown at the time the function is instantiated, /even if/ was declared using a template. What am I missing?
Right. That was additional information I lost somewhere. That's one thing I've heard bandied around here, that structs are passed by value and classes by reference. It seems too peculiar to be true. Anyway, my comment was based on the understanding that the parameter is passed by reference. A reference is just a pointer. You don't need to know anything about what it points to until you dereference it. E.g. foo(opaque* myvar) { if (this.doIt == true) bar(myvar); } Can be compiled without ever knowing anything about the opaque type. Obviously if its passed by value you know the size because your call pushed it on the stack. Bruce.
Sep 18 2007
parent Nathan Reed <nathaniel.reed gmail.com> writes:
Bruce Adams wrote:
 That's one thing I've heard bandied around here, that structs are passed by
value and classes by reference. It seems too peculiar to be true. 
It's true. Why do you find this peculiar? Structs are aggregate data structures with value semantics, and classes are aggregate data structures with reference semantics. For some problems you want one, and for some you want the other. Thanks, Nathan Reed
Sep 18 2007
prev sibling parent reply Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
Bill Baxter Wrote:

[snip]
 
 There would still be a bit of a compiler compatibility issue though. 
 For a given platform Compiler vendors A and B would need to agree on the 
 rules for 'in' parameters or the libs they generate would be 
 incompatible.  If we want D to be a language with a unified ABI at 
 least.  That suggests that the rules would need to be part of the spec. 
   Heck it may be good enough to just pick a max parameter size and stick 
 with it for all platforms.  Just like D picks a size for 'float' and 
 sticks with it for all platforms, whether it's the most efficient for 
 that platform or not.
 
 --bb
That's not the same at all. There are internationally approved standards for floating point types. These standards are also implemented in hardware more often that not. Bruce.
Sep 18 2007
parent Bill Baxter <dnewsgroup billbaxter.com> writes:
Bruce Adams wrote:
 Bill Baxter Wrote:
 
 [snip]
 There would still be a bit of a compiler compatibility issue though. 
 For a given platform Compiler vendors A and B would need to agree on the 
 rules for 'in' parameters or the libs they generate would be 
 incompatible.  If we want D to be a language with a unified ABI at 
 least.  That suggests that the rules would need to be part of the spec. 
   Heck it may be good enough to just pick a max parameter size and stick 
 with it for all platforms.  Just like D picks a size for 'float' and 
 sticks with it for all platforms, whether it's the most efficient for 
 that platform or not.

 --bb
That's not the same at all. There are internationally approved standards for floating point types. These standards are also implemented in hardware more often that not.
Any Walter-approved ABI standards for the D compiler are basically "internationally approved" too. But maybe a better example would have been "just like D picks 64 bits for a long and sticks with it whether it's efficient or not". Whatever. This is really not important. --bb
Sep 18 2007
prev sibling next sibling parent reply Jascha Wetzel <firstname mainia.de> writes:
Janice Caron wrote:
 Lets let "ref" mean "the function may modify the parameter", and the
 absense of ref mean "the function may not modify the parameter", but
 leave it to the compiler to decide what is the most efficient way to
 pass the data to the function.
 
 I think this is a neat idea, however it won't work unless the compiler
 can also do const-checking. This is one more optimisation which having
 const allows. (And const-by-default would make it obvious).
we still need a solution to force pass-by-value and pass-by-reference for exported functions that are called from non-D code.
 ...
 would no longer compile, because under the new scheme, "ref" is
 reinterpreted to mean that c is head-const (because C is a class - see
 definitions in previous post). So instead, you'd have to do:
 
 class C;
 C c;
 f(&c)
 
 void f(ref C* c)
 {
     *c = new C();
 }
 
 Whether or not that would be considered a prohibiting problem, I don't know.
if you define ref to mean "be able to change the cell the symbol represents", that isn't needed.
Sep 18 2007
parent "Janice Caron" <caron800 googlemail.com> writes:
On 9/18/07, Jascha Wetzel <firstname mainia.de> wrote:
 if you define ref to mean "be able to change the cell the symbol
 represents", that isn't needed.
What I mean was defined in a previous post - hopefully fairly precisely. It was too big a definition to repeat here.
Sep 18 2007
prev sibling parent reply renoX <renosky free.fr> writes:
Janice Caron a écrit :
 About passing structs by reference: It's one of those things, like
 register or inline...
 
 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.
 
 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.
Well in some (very limited) cases, they still do: I'm thinking about the Linux kernels where developers do care and know (at least in some parts of the kernel) what the compiler do, of course this is not the usual case.. That said, I know that Sun's JVM has some 'escape analysis' optimisation (or it's planned to have this) where it's able to put on the stack some of the objects, but I don't know how efficient it is in comparison to the developer choosing heap or stack allocation.. renoX
Sep 18 2007
parent Bruce Adams <tortoise_74 yeah.who.co.uk> writes:
renoX Wrote:

 Janice Caron a écrit :
 About passing structs by reference: It's one of those things, like
 register or inline...
 
 Once upon a time, programmers used the keyword "register" because they
 thought they could do a better job than the compiler at figuring how
 to use its registers.
 
 Once upon a time, programmers used the keyword "inline" because they
 thought they could do a better job than the compiler at figuring out
 what to inline and what not.
Well in some (very limited) cases, they still do: I'm thinking about the Linux kernels where developers do care and know (at least in some parts of the kernel) what the compiler do, of course this is not the usual case..
Its not just in the Kernel. It comes up relatively frequently when dealing with large datasets using the STL. If your profile says a small function its taking the most time then inlining it can help. The compiler doesn't know and in general can't know how your code is going to be used, you need the execution profile for that. Bruce.
Sep 18 2007