Re: const assignments problem again

2011-08-20 Thread bearophile
Kagamin:

> P.S. you would make a better point if it were a const instance of a class.

You can't even define V as "const struct" and use it like this:


const struct V { double x, y; } // wrong
void main() {
double x = 1, y = 2;
int c = 1;
V v;
switch (c) {
case 1:
v = V(x, y);
break;
case 2:
v = V(-x, -y);
break;
// other cases here
default: assert(0);
}
}


This works (the asm shows DMD doesn't inline the function):

const struct V { double x, y; }
void main() {
double x = 1, y = 2;
int c = 1;
V v = {
switch (c) {
case 1:  return V(x, y);
case 2:  return V(-x, -y);
// other cases here
default: assert(0);
}
}();
}

Bye,
bearophile


Re: const assignments problem again

2011-08-09 Thread Jacob Carlborg

On 2011-08-09 07:04, Don wrote:

Jacob Carlborg wrote:

On 2011-08-07 03:19, bearophile wrote:

I have discussed about this topic once in past, but in the meantime I
have seen this is a quite common problem, so I think it doesn't harm
to touch this topic again.

This is a direct D translation of the original C or C++ code:


// version #1
double foo;
if (abs(e.x - v.x)> double.min)
foo = (v.y - e.y) / (v.x - e.x);
else
foo = double.max;


If D's statements were expressions instead, this could work:

const foo = if (abs(e.x - v.x)> double.min)
(v.y - e.y) / (v.x - e.x);
else
double.max;



I find that much more difficult to read.
Especially consider

const foo = (a > b) ? bar() : baz();

compared to

const foo = if (a > b)
bar();
else
baz();

You have to read quite a lot of code before you get any visual cue that
the return value of baz() is used.
IMHO: to understand code, I think you really need to know if you're
looking at an expression or a statement, so making 'if' do both jobs
reduces code clarity. (Would be OK in a language where it was _always_
an expression).


I think this syntax fits when using a ternary operator would be too long 
and creating one (or two) new function(s) would just be annoying. But if 
the if-statement wasn't an expression in all cases it would be very 
confusing. I see no reason why many of the statements in D couldn't be 
expressions instead, but I can understand that it's way too late to 
change that now.


--
/Jacob Carlborg


Re: const assignments problem again

2011-08-08 Thread Don

Jacob Carlborg wrote:

On 2011-08-07 03:19, bearophile wrote:
I have discussed about this topic once in past, but in the meantime I 
have seen this is a quite common problem, so I think it doesn't harm 
to touch this topic again.


This is a direct D translation of the original C or C++ code:


// version #1
double foo;
if (abs(e.x - v.x)>  double.min)
 foo = (v.y - e.y) / (v.x - e.x);
else
 foo = double.max;


If D's statements were expressions instead, this could work:

const foo = if (abs(e.x - v.x)>  double.min)
(v.y - e.y) / (v.x - e.x);
else
double.max;



I find that much more difficult to read.
Especially consider

const foo  =  (a > b) ? bar() : baz();

compared to

const foo = if (a > b)
bar();
else
baz();

You have to read quite a lot of code before you get any visual cue that 
the return value of baz() is used.
IMHO: to understand code, I think you really need to know if you're 
looking at an expression or a statement, so making 'if' do both jobs 
reduces code clarity. (Would be OK in a language where it was _always_ 
an expression).


Re: const assignments problem again

2011-08-07 Thread Jacob Carlborg

On 2011-08-07 03:19, bearophile wrote:

I have discussed about this topic once in past, but in the meantime I have seen 
this is a quite common problem, so I think it doesn't harm to touch this topic 
again.

This is a direct D translation of the original C or C++ code:


// version #1
double foo;
if (abs(e.x - v.x)>  double.min)
 foo = (v.y - e.y) / (v.x - e.x);
else
 foo = double.max;


If D's statements were expressions instead, this could work:

const foo = if (abs(e.x - v.x)>  double.min)
(v.y - e.y) / (v.x - e.x);
else
double.max;

--
/Jacob Carlborg


Re: const assignments problem again

2011-08-07 Thread Lutger Blijdestijn
Timon Gehr wrote:

> Lutger Blijdestin wrote:
>> I like the ternary operator the best for this, as it is the simplest. I
>> always write them like this though, liberally include parenthesis and
>> never nest:
>>  (condition)
>> ? (truth value)
>> : (false value)
> 
> What is the benefit of this, compared to leaving parentheses away
> entirely?

Oh, I didn't mean to *always* include parenthesis. Just that when a more 
complicated expression is involved, I find it often faster to understand if 
there are some redundant parenthesis. 


Re: const assignments problem again

2011-08-07 Thread Timon Gehr
Lutger Blijdestin wrote:
> I like the ternary operator the best for this, as it is the simplest. I
> always write them like this though, liberally include parenthesis and never
> nest:
>  (condition)
> ? (truth value)
> : (false value)

What is the benefit of this, compared to leaving parentheses away entirely?


Re: const assignments problem again

2011-08-07 Thread Lutger Blijdestijn
bearophile wrote:
... 
> In my precedent post about this topic I have discussed "nested constness"
> and another partially silly idea. Recently I have seen a language that
> suggests me this:
> 
> // version #6
> const foo;
> if (abs(e.x - v.x) > double.min)
> foo = (v.y - e.y) / (v.x - e.x);
> else
> foo = double.max;
> 
> 
> The compiler makes sure all paths assign values with the same type to foo
> (as in the case of the two returns inside the delegate, that must be of
> the same type). But if you introduce goto instructions version #6 looks
> fragile. If the assign is far away from the definition the code looks not
> so nice any more, so this feature is meant for short-range initializations
> only.
> 
> Bye,
> bearophile

I like the ternary operator the best for this, as it is the simplest. I 
always write them like this though, liberally include parenthesis and never 
nest:
 (condition)
? (truth value)
: (false value)

When it gets more complicated, you can always rewrite either the whole 
expression (to if/else or a function) or refactor parts of the expression to 
small functions. I never find this to be much of a burden.

Python has if/else expressions, but due to it's (messy) scoping rules I 
almost never find them an improvement to if/else statements.

I like the single assignment feature, but not for this reason. I think it 
would be more useful for creating immutable data in general.



Re: const assignments problem again

2011-08-07 Thread Jonathan M Davis
On Sunday 07 August 2011 06:02:36 Kagamin wrote:
> Jonathan M Davis Wrote:
> > Being able to use const can be very valuable. For instance, what if you
> > were using std.algorithm.copy and got the arguments backwards? If the
> > source is const, then the compiler will complain, and you'll quickly
> > find the bug. If the source isn't const, then you could accidentally
> > end up copying the target to the source, and it may or may not be an
> > easy bug to catch.
> 
> I've seen a bug. I fixed two methods: begin and end and sent the patch to a
> man. They were calling other methods (sort of begin1 and end1). The man
> slightly fixed my patch and copied the fixed body of the first method to
> both of them (they look very similar). The compiler was happy, the
> application even worked. The bug was catched only because I checked whether
> the man did it right.

??? I don't understand what you're trying to say. I don't know if your English 
is just poor or if you're trying to make fun of me.

const has value. It obviously doesn't solve everything or catch every bug, but 
it can help a lot in ensuring that variables that are not supposed to be 
mutated aren't mutated. You can choose not to use it, and that's fine, but it's 
part of the language, and many other people value it highly.

- Jonathan M Davis


Re: const assignments problem again

2011-08-07 Thread Kagamin
Jonathan M Davis Wrote:

> Being able to use const can be very valuable. For instance, what if you were 
> using std.algorithm.copy and got the arguments backwards? If the source is 
> const, then the compiler will complain, and you'll quickly find the bug. If 
> the 
> source isn't const, then you could accidentally end up copying the target to 
> the source, and it may or may not be an easy bug to catch.

I've seen a bug. I fixed two methods: begin and end and sent the patch to a 
man. They were calling other methods (sort of begin1 and end1). The man 
slightly fixed my patch and copied the fixed body of the first method to both 
of them (they look very similar). The compiler was happy, the application even 
worked. The bug was catched only because I checked whether the man did it right.


Re: const assignments problem again

2011-08-07 Thread Kagamin
bearophile Wrote:

> - You can't use "auto" there, so if the type of e.x changes, you have to 
> change the foo type manually (double.min is replaceable with typeof(e.x).min).

This is a bad idea too. The code assumes the values are double. If this 
assumption is not true, the code is broken.


Re: const assignments problem again

2011-08-07 Thread Jonathan M Davis
On Sunday 07 August 2011 04:40:52 Kagamin wrote:
> bearophile Wrote:
> > - And foo can't be const or immutable, I don't like this.
> 
> I suppose accidental overwrite bugs are overrated. I have never seen them
> even from evil programmers. If you write random code, overwrite is not your
> only problem, you can as well read wrong variable or call wrong function.
> No language will help you if your code is junk. You should fix this in a
> different way.

Being able to use const can be very valuable. For instance, what if you were 
using std.algorithm.copy and got the arguments backwards? If the source is 
const, then the compiler will complain, and you'll quickly find the bug. If the 
source isn't const, then you could accidentally end up copying the target to 
the source, and it may or may not be an easy bug to catch. I made that exact 
mistake in C++ with its copy function just the other day. const saved me a lot 
of headaches.

Now, I think that D gives us enough ways to deal with the problem that 
Bearophile illustrates here (and Bearophile actually showed us a number of 
ways that D allows us to do what he's trying to do), so I don't think that we 
really need to do anything to the language to better deal with this situation. 
But accidental overwrites _can_ be a problem, and that's one of the things 
that const catches. So, not being able to use const when you should logically 
be able to due to syntax problems in the language would definitely be a problem 
- not the biggest problem ever perhaps, but it _would_ be a problem. 
Fortunately however, D gives us plenty of ways to get around the problem.

- Jonathan M Davis


Re: const assignments problem again

2011-08-07 Thread Kagamin
bearophile Wrote:

> - And foo can't be const or immutable, I don't like this.

I suppose accidental overwrite bugs are overrated. I have never seen them even 
from evil programmers. If you write random code, overwrite is not your only 
problem, you can as well read wrong variable or call wrong function. No 
language will help you if your code is junk. You should fix this in a different 
way.


Re: const assignments problem again

2011-08-07 Thread Kagamin
P.S. you would make a better point if it were a const instance of a class.


Re: const assignments problem again

2011-08-07 Thread Marco Leise
Am 07.08.2011, 06:31 Uhr, schrieb Adam D. Ruppe  
:



bearophile:

I agree, that's why I have added two more points


The precedence of the ternary is pretty easy to handle - if there's
more than one thing in there, just use parenthesis. That's the
only thing I can think of that would make it any more bug prone
than other branches.

Perhaps it'd be worth thinking if parens should be required in any
non-trivial cases.

auto a = something ? "one" : "two"; // ok

auto b = a ~ something ? "one" : "two"; // error, please add parens for  
clarity



- It is less maintainable


I don't agree that changing to an if is any burden at all. This
is like saying "if(x) y;" is unacceptable because you might have
to add braces later.

Real maintenance headaches are things like duplicated code
or unnecessary complexity. This is just a very simple case of
syntax translation.


The anonymous function solution looks good to me. You have full  
flexibility while keeping the variable const and the initialization code  
is at the declaration site.


Re: const assignments problem again

2011-08-06 Thread Adam D. Ruppe
bearophile:
> I agree, that's why I have added two more points

The precedence of the ternary is pretty easy to handle - if there's
more than one thing in there, just use parenthesis. That's the
only thing I can think of that would make it any more bug prone
than other branches.

Perhaps it'd be worth thinking if parens should be required in any
non-trivial cases.

auto a = something ? "one" : "two"; // ok

auto b = a ~ something ? "one" : "two"; // error, please add parens for clarity

> - It is less maintainable

I don't agree that changing to an if is any burden at all. This
is like saying "if(x) y;" is unacceptable because you might have
to add braces later.

Real maintenance headaches are things like duplicated code
or unnecessary complexity. This is just a very simple case of
syntax translation.


Re: const assignments problem again

2011-08-06 Thread bearophile
Adam D. Ruppe:

> > I don't like it.
> 
> That's not good enough.

I agree, that's why I have added two more points:
- conditional expression is bug-prone and it's a bit tricky (it's a common 
source of various bugs), because of it operator precedence too;
- It is less maintainable (if you want to add something you sometimes need to 
convert it again into a normal if).

Bye,
bearophile


Re: const assignments problem again

2011-08-06 Thread Adam D. Ruppe
Reading this post, I kept thinking "why aren't you just using
the ternary operator?"

> I don't like it.

That's not good enough.


const assignments problem again

2011-08-06 Thread bearophile
I have discussed about this topic once in past, but in the meantime I have seen 
this is a quite common problem, so I think it doesn't harm to touch this topic 
again.

This is a direct D translation of the original C or C++ code:


// version #1
double foo;
if (abs(e.x - v.x) > double.min)
foo = (v.y - e.y) / (v.x - e.x);
else
foo = double.max;


This version is clear, easy to understand, efficient, and it's not bug-prone. 
Some coding standards require those to use full braces there. But it has some 
problems too:
- It's not DRY, "foo" is repeated three times;
- You can't use "auto" there, so if the type of e.x changes, you have to change 
the foo type manually (double.min is replaceable with typeof(e.x).min).
- It's not short code.
- And foo can't be const or immutable, I don't like this.


In this specific example one of the two branches of the if contains just a 
constant, so you are allowed to write:

// version #2
double foo = double.max;
if (abs(e.x - v.x) > double.min)
foo = (v.y - e.y) / (v.x - e.x);


But generally you can't do that because the then-else branches of the if are 
meant to be computed lazily, only one of them.


To turn foo constant, you are free to use a temporary mutable variable, but 
this makes the local namespace even more dirty:

// version #3
double _blue;
if (abs(e.x - v.x) > double.min)
foo = (v.y - e.y) / (v.x - e.x);
else
foo = double.max;
immutable foo = _blue;


In D you are allowed to create a function and call it in place:

// version #4
const foo = {
if (abs(e.x - v.x) > double.min)
return (v.y - e.y) / (v.x - e.x);
else
return double.max;
}();


Version #4 has some downsides:
- The return type of this delegate is inferenced. This means that both branches 
of the if must return the same type. Currently in D this disallows some 
possibilities (I hope this will be fixed), in some cases you have to cast the 
empty result to the same type of the other if branch;
- The code is even longer and I don't like its look a lot (in JavaScript it's 
fine);
- It's not certain that every D compiler will always inline that delegate. This 
risks lower performance;
- That delegate uses values defined outside it, so it can't be pure, so the 
function that contains such code can't be pure. You solve this problem making 
the code more complex, passing the needed local variables as arguments to the 
delegate, but this is not handy.


Version #5 is an acceptable solution, it is compact and it defines just one 
variable, that is constant, but the C conditional expression is bug-prone and 
it's a bit tricky (it's a common source of various bugs), I don't like it. This 
code is less maintainable (if you want to add something you sometimes need to 
convert it again into a normal if). 

// version #5
const foo = (abs(e.x - v.x) > double.min) ?
 ((v.y - e.y) / (v.x - e.x)) :
 double.max;


In my precedent post about this topic I have discussed "nested constness" and 
another partially silly idea. Recently I have seen a language that suggests me 
this:

// version #6
const foo;
if (abs(e.x - v.x) > double.min)
foo = (v.y - e.y) / (v.x - e.x);
else
foo = double.max;


The compiler makes sure all paths assign values with the same type to foo (as 
in the case of the two returns inside the delegate, that must be of the same 
type). But if you introduce goto instructions version #6 looks fragile. If the 
assign is far away from the definition the code looks not so nice any more, so 
this feature is meant for short-range initializations only.

Bye,
bearophile