www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - BigInt bool assign

reply bearophile <bearophileHUGS lycos.com> writes:
Here I ask for opinions about a small enhancement request about BigInt that Don
has refused:

http://d.puremagic.com/issues/show_bug.cgi?id=7079

It's not a very important thing, so I will probably avoid further arguing after
this post :-)


In some languages like the ones derived from Pascal, like Ada, and in some
other languages Java the boolean type is very distinct from the integer values.
So to test if a integer value is zero you write something like:


int x = 10;
if (x == 0) {
    // do A ...
} else {
    // do B ...
}


In languages derived from C, like C++ and D and many others, like Python too,
the integer values are valid in a boolean context too. This is valid C/C++/D
code:

int x = 10;
if (x) {
    // do B ...
} else {
    // do A ...
}



This is handy in some situations, like when you want to count how many true
cases there are:


void main() {
    auto s1 = "hello";
    auto s2 = "hallo";
    int hamming_distance = 0;
    assert(s1.length == s2.length);
    foreach (i, c1; s1)
        hamming_distance += c1 != s2[i];
    assert(hamming_distance == 1);
}


While in a Delphi/Java language you need something like:

if (c1 != s2[i])
    hamming_distance++;


The implicit conversion from boolean to integer is equally handy in Python:

s1 = "hello"
s2 = "hallo"
hamming_distance = sum(c1 != c2 for c1,c2 in zip(s1, s2))
assert hamming_distance == 1


D language regards boolean values as a subset of integers so it allows implicit
conversion from bool to integer, but not from int to bool. I don't think this
will ever change in D2/D3:


void main() {
    int x = 1;
    bool b = true;
    x = b;         // bool -> int is OK
    int y = x > 3; // bool -> int is OK
    b = x;         // int -> bool is an Error
}


While multi-precision numbers are not the fixed size integers, it is wise to
give multi-precision numbers the same rules and usages of the normal fixed size
integers _everywhere this is possible and handy_. This has some advantages like:
- Reduces the cognitive burden to remember where they differ;
- Allows for less work to adapt routines that work with integers to work with
BigInts. This is handy for generic code and for manual translation of code.

I have said everywhere this is possible and handy, because this is not always
possible. You can't use a BigInt to index an array, and there are some
situations where BigInts require a different algorithm (example:
http://d.puremagic.com/issues/show_bug.cgi?id=7102 ). So I am not asking BigInt
to be a drop-in replacement for int in all cases.
But I have seen a hundred cases where in Python it's handy to use the built-in
multi-precision integers with normal algorithms useful for normal integers too.


So I have asked to allow implicit bool -> BigInt too:


import std.bigint;
void main() {
    BigInt b = true;
}


This allows BigInt to be used as an int in a situation where it causes no harm.
Introducing an usage difference here between int and BigInt in my opinion is
gratuitous, doesn't help reduce bugs, it asks the programmer to remember one
difference between them that gives nothing useful back. So that code should be
accepted.

Bye,
bearophile
Dec 23 2011
next sibling parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12/23/2011 01:42 PM, bearophile wrote:
 This allows BigInt to be used as an int in a situation where it causes no
harm. Introducing an usage difference here between int and BigInt in my opinion
is gratuitous, doesn't help reduce bugs, it asks the programmer to remember one
difference between them that gives nothing useful back. So that code should be
accepted.

 Bye,
 bearophile

+1.
Dec 23 2011
prev sibling next sibling parent reply "Paul D. Anderson" <paul.d.removethis.anderson comcast.andthis.net> writes:
FWIW, I've just added logical operations to my decimal number 
library (https://github.com/andersonpd/decimal) and boolean 
interoperability arose as a byproduct.

 From std.bigint docs:

"All arithmetic operations are supported, except unsigned shift 
right (>>>). Logical operations are not currently supported."

Maybe when support for logical ops are added support for booleans 
will be introduced for consistency.

On Friday, 23 December 2011 at 12:42:24 UTC, bearophile wrote:
 Here I ask for opinions about a small enhancement request about 
 BigInt that Don has refused:

 http://d.puremagic.com/issues/show_bug.cgi?id=7079

 It's not a very important thing, so I will probably avoid 
 further arguing after this post :-)


 In some languages like the ones derived from Pascal, like Ada, 
 and in some other languages Java the boolean type is very 
 distinct from the integer values. So to test if a integer value 
 is zero you write something like:


 int x = 10;
 if (x == 0) {
   // do A ...
 } else {
   // do B ...
 }


 In languages derived from C, like C++ and D and many others, 
 like Python too, the integer values are valid in a boolean 
 context too. This is valid C/C++/D code:

 int x = 10;
 if (x) {
   // do B ...
 } else {
   // do A ...
 }



 This is handy in some situations, like when you want to count 
 how many true cases there are:


 void main() {
   auto s1 = "hello";
   auto s2 = "hallo";
   int hamming_distance = 0;
   assert(s1.length == s2.length);
   foreach (i, c1; s1)
       hamming_distance += c1 != s2[i];
   assert(hamming_distance == 1);
 }


 While in a Delphi/Java language you need something like:

 if (c1 != s2[i])
   hamming_distance++;


 The implicit conversion from boolean to integer is equally 
 handy in Python:

 s1 = "hello"
 s2 = "hallo"
 hamming_distance = sum(c1 != c2 for c1,c2 in zip(s1, s2))
 assert hamming_distance == 1


 D language regards boolean values as a subset of integers so it 
 allows implicit conversion from bool to integer, but not from 
 int to bool. I don't think this will ever change in D2/D3:


 void main() {
   int x = 1;
   bool b = true;
   x = b;         // bool -> int is OK
   int y = x > 3; // bool -> int is OK
   b = x;         // int -> bool is an Error
 }


 While multi-precision numbers are not the fixed size integers, 
 it is wise to give multi-precision numbers the same rules and 
 usages of the normal fixed size integers _everywhere this is 
 possible and handy_. This has some advantages like:
 - Reduces the cognitive burden to remember where they differ;
 - Allows for less work to adapt routines that work with 
 integers to work with BigInts. This is handy for generic code 
 and for manual translation of code.

 I have said everywhere this is possible and handy, because this 
 is not always possible. You can't use a BigInt to index an 
 array, and there are some situations where BigInts require a 
 different algorithm (example: 
 http://d.puremagic.com/issues/show_bug.cgi?id=7102 ). So I am 
 not asking BigInt to be a drop-in replacement for int in all 
 cases.
 But I have seen a hundred cases where in Python it's handy to 
 use the built-in multi-precision integers with normal 
 algorithms useful for normal integers too.


 So I have asked to allow implicit bool -> BigInt too:


 import std.bigint;
 void main() {
   BigInt b = true;
 }


 This allows BigInt to be used as an int in a situation where it 
 causes no harm. Introducing an usage difference here between 
 int and BigInt in my opinion is gratuitous, doesn't help reduce 
 bugs, it asks the programmer to remember one difference between 
 them that gives nothing useful back. So that code should be 
 accepted.

 Bye,
 bearophile

Dec 23 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Paul D. Anderson:

  From std.bigint docs:
 
 "All arithmetic operations are supported, except unsigned shift 
 right (>>>). Logical operations are not currently supported."
 
 Maybe when support for logical ops are added support for booleans 
 will be introduced for consistency.

Don has closed my tiny enhancement request, he doesn't want to change that. Bye, bearophile
Dec 23 2011
prev sibling next sibling parent "Mr. Anonymous" <mailnew4ster gmail.com> writes:
On 23.12.2011 14:42, bearophile wrote:
 D language regards boolean values as a subset of integers so it allows
implicit conversion from bool to integer, but not from int to bool. I don't
think this will ever change in D2/D3:

Discussing about pros and cons of such implicit conversion is one thing, but that's already how it works for basic types, so I agree it must work for more advanced ones. After all, BigInt is a big int, and it should feel like that. If one expects a += (b < c); to work for an int (and it works), it should work for BigInts as well. Another option is also disabling it for basic types, but better not define different behavior for similar types.
Dec 23 2011
prev sibling next sibling parent reply Derek <ddparnell bigpond.com> writes:
On Fri, 23 Dec 2011 23:42:24 +1100, bearophile <bearophileHUGS lycos.com>  
wrote:

 Here I ask for opinions about a small enhancement request about BigInt  
 that Don has refused ...

I'm with Don on this one because a boolean and an integer are not the same concept, and even though many programming languages implement booleans using integers, it still doesn't make them the same thing. Using booleans as implicit integers can be seen as laziness (i.e. poor documentation of coder's intent) or a legitimate mistake (i.e unintentional usage by coder). By insisting that an explicit cast must be used when one wants a boolean to behave as an integer allows the coder's intent to become more apparent when reading their source code. This has nothing to do with machine code generation, just source code legibility. -- Derek Parnell Melbourne, Australia
Dec 23 2011
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Derek Parnell:

 I'm with Don on this one because a boolean and an integer are not the same  
 concept, and even though many programming languages implement booleans  
 using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs. Not allowing a BigInt to be initialized with a bool value introduce an inconsistency that makes BigInts more complex because there is one more rule to remember, less inter-operable with ints, and I don't think it introduces advantages.
 Using booleans as implicit integers can be seen as laziness (i.e. poor  
 documentation of coder's intent) or a legitimate mistake (i.e  
 unintentional usage by coder).

In my code such mistakes are uncommon.
 By insisting that an explicit cast must be  
 used when one wants a boolean to behave as an integer allows the coder's  
 intent to become more apparent when reading their source code. This has  
 nothing to do with machine code generation, just source code legibility.

Casts are powerful tools, they shut up the compiler and they assume the programmer is perfectly right and has perfect knowledge of what's going on. In practice my experience shows that the programmer (me too) sometimes doesn't have perfect knowledge (usually because the code later was modified, turning the cast into a bug because casts are often silent). This is why it's better to avoid casts, not requiring them in the first place, unless they are useful. In this case I think a cast introduces more danger than the risks caused by implicit bool->int conversions. Bye, bearophile
Dec 23 2011
next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/23/2011 11:34 PM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions

Using an expression in a conditional amounts to an explicit cast. This is an unrelated issue. Implicit int -> bool conversion is disallowed in D. It loses information.
 - but converting from bool to int is something else entirely. I see no reason
to expand that
 problem into BigInt. _int_ shouldn't have it, let alone BigInt.

 - Jonathan M Davis

There is really no problem with that. I have never seen anyone complain about implicit bool -> int conversion. Why do you think it is bad? Does anyone have an example to back up the claim that it is bad?
Dec 23 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12/24/2011 12:31 AM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 23:52:00 Timon Gehr wrote:
 There is really no problem with that. I have never seen anyone complain
 about implicit bool ->  int conversion. Why do you think it is bad? Does
 anyone have an example to back up the claim that it is bad?

They're completely different types and mean completely different things. It's one thing to convert from a narrower integer to a wider one, but bool is _not_ an integer. Would you implicitly convert a string to an int? No. It's not a number. I don't see any reason to treat bool any differently on that count. bool isn't a number either. It's true or it's false. The problem is that C conflated bool with int, and on some level that behavior still exists in D. But bool and int are two entirely different types and entirely different concepts. - Jonathan M Davis

Entirely different concepts? oO bool and int are in no way 'entirely different concepts'. Both are fields. bool is (Z_2, ^, &) , int is (Z_(2^32), +, *). string is conceptually a monoid. Boolean algebra is the algebra of two values. At least in computer science or digital design, those two values are 0 and 1. If there are implicit conversions in a language at all, implicit bool -> int is a natural thing to do. There is no such argument for string -> int.
Dec 23 2011
prev sibling next sibling parent Peter Alexander <peter.alexander.au gmail.com> writes:
On 23/12/11 10:34 PM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions - but converting from bool to int is something else entirely. I see no reason to expand that problem into BigInt. _int_ shouldn't have it, let alone BigInt.

I agree that bool -> int is wrong, but I also think that inconsistency between int and BigInt is wrong.
Dec 23 2011
prev sibling next sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/23/2011 11:34 PM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions - but converting from bool to int is something else entirely. I see no reason to expand that problem into BigInt. _int_ shouldn't have it, let alone BigInt. - Jonathan M Davis

A: "Um, so why does bool implicitly convert to int but not to BigInt?" B: "Because the language's design contains an error. It is a huge _problem_. Therefore we decided to keep it inconsistent. If you re-parenthesise your expression however, your code will compile." A: "Awesome!!"
Dec 23 2011
parent reply Don <nospam nospam.com> writes:
On 24.12.2011 01:32, Timon Gehr wrote:
 On 12/23/2011 11:34 PM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions - but converting from bool to int is something else entirely. I see no reason to expand that problem into BigInt. _int_ shouldn't have it, let alone BigInt. - Jonathan M Davis

A: "Um, so why does bool implicitly convert to int but not to BigInt?" B: "Because the language's design contains an error. It is a huge _problem_. Therefore we decided to keep it inconsistent. If you re-parenthesise your expression however, your code will compile." A: "Awesome!!"

As I said when I closed that post, it is _impossible_ for BigInt to always behave the same as int. One example: byte c = x & 0x7F; This compiles if x is an int. It doesn't compile if x is a BigInt. BigInt's job is to behave like a Euclidean integer, not to be a drop-in replacement for built-in integer types.
Dec 24 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
My post seems to have been lost because of NG malfunction.

On 12/24/2011 09:17 AM, Don wrote:
 On 24.12.2011 01:32, Timon Gehr wrote:
 On 12/23/2011 11:34 PM, Jonathan M Davis wrote:
 On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions - but converting from bool to int is something else entirely. I see no reason to expand that problem into BigInt. _int_ shouldn't have it, let alone BigInt. - Jonathan M Davis

A: "Um, so why does bool implicitly convert to int but not to BigInt?" B: "Because the language's design contains an error. It is a huge _problem_. Therefore we decided to keep it inconsistent. If you re-parenthesise your expression however, your code will compile." A: "Awesome!!"

As I said when I closed that post, it is _impossible_ for BigInt to always behave the same as int. One example: byte c = x & 0x7F; This compiles if x is an int. It doesn't compile if x is a BigInt.


This is a limitation of the language. No case can be made that this conversion would not be desirable, and it is an issue that is not related a lot to what is being discussed in this thread. By the way, is this also the rationale for why BigInt and long/ulong cannot be compared for equality?
 BigInt's job is to behave like a Euclidean integer, not to be a drop-in
 replacement for built-in integer types.


It is not any more or any less valid to build an Euclidean integer from a residue class than from a bool. The main problem I have with bool -> int ok, int -> BigInt ok, bool -> BigInt NG is that it kills the transitivity of the implicitly-converts-to relation, not that I want to use BigInt as a drop-in replacement for int. By the way, bool does not implicitly convert to BigInt because std.bigint contains a bug, not because it explicitly disallows it. Negation does not work for bool therefore it does not compile even though it would be unreachable code. void opAssign(T: long)(T x) // T could be bool { data = cast(ulong)((x < 0) ? -x : x); // does not work for bool sign = (x < 0); } This has other interesting implications: struct S{ long x; alias x this; void opUnary()(){} } void main() { import std.bigint; S s; BigInt x = s; // NG! }
Dec 24 2011
prev sibling parent reply Don <nospam nospam.com> writes:
On 24.12.2011 02:30, Derek wrote:
 On Sat, 24 Dec 2011 09:19:26 +1100, bearophile
 <bearophileHUGS lycos.com> wrote:

 This is why it's better to avoid casts, not requiring them in the
 first place, unless they are useful. In this case I think a cast
 introduces more danger than the risks caused by implicit bool->int
 conversions.

If we assume that explicit casts are required for bool->int conversion, can you show some code in which this could cause a problem?

I think stuff like int z += x > y; should ideally require a cast. That's a crazy operation. The problem is compatibility with ancient C code (pre-C99), where you may find: alias int BOOL; BOOL b = x > y; Although BOOL is typed as 'int', it really has the semantics of 'bool'. We have an example of this in D1's opEquals(). I think this is reason why implicit conversion bool -> int exists. BTW, great to see you again, Derek!
Dec 24 2011
next sibling parent reply "Mr. Anonymous" <mailnew4ster gmail.com> writes:
On 24.12.2011 12:33, Don wrote:
 On 24.12.2011 02:30, Derek wrote:
 On Sat, 24 Dec 2011 09:19:26 +1100, bearophile
 <bearophileHUGS lycos.com> wrote:

 This is why it's better to avoid casts, not requiring them in the
 first place, unless they are useful. In this case I think a cast
 introduces more danger than the risks caused by implicit bool->int
 conversions.

If we assume that explicit casts are required for bool->int conversion, can you show some code in which this could cause a problem?

I think stuff like int z += x > y; should ideally require a cast. That's a crazy operation. The problem is compatibility with ancient C code (pre-C99), where you may find: alias int BOOL; BOOL b = x > y; Although BOOL is typed as 'int', it really has the semantics of 'bool'. We have an example of this in D1's opEquals(). I think this is reason why implicit conversion bool -> int exists. BTW, great to see you again, Derek!

The D Programming Language, page 172: for (; n >= iter * iter; iter += 2 - (iter == 2)) { ... :)
Dec 24 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/24/11 6:46 AM, Mr. Anonymous wrote:
 The D Programming Language, page 172:
 for (; n >= iter * iter; iter += 2 - (iter == 2)) { ...
 :)

And proud of every letter of it! Andrei
Dec 24 2011
prev sibling next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Don:

 As I said when I closed that post, it is _impossible_ for BigInt to
 always behave the same as int. One example:
 
 byte c = x & 0x7F;
 
 This compiles if x is an int. It doesn't compile if x is a BigInt.
 
 BigInt's job is to behave like a Euclidean integer, not to be a drop-in
 replacement for built-in integer types.

As I have said in the first post of this thread I am not asking for impossible things:
 While multi-precision numbers are not the fixed size integers, it is wise to
give
 multi-precision numbers the same rules and usages of the normal fixed size
integers
 _everywhere this is possible and handy_. This has some advantages like:
 - Reduces the cognitive burden to remember where they differ;
 - Allows for less work to adapt routines that work with integers to work with
 BigInts. This is handy for generic code and for manual translation of code.
 
 I have said everywhere this is possible and handy, because this is not always
 possible. You can't use a BigInt to index an array, and there are some
 situations where BigInts require a different algorithm
 (example: http://d.puremagic.com/issues/show_bug.cgi?id=7102 ).
 So I am not asking
 BigInt to be a drop-in replacement for int in all cases.

Despite this code is currently not accepted: BigInt x; byte c = x & 0x7F; Refusing this too introduces another useless difference between ints and BigInts: void main() { BigInt b = true; } Introducing differences between the two types is acceptable if it's required by the semantic difference between the two types, or if it introduces some other improvement. But this is not the case. So this argument of yours is invalid. ------------------------- Derek Parnell:
 In my code such mistakes are uncommon.


 But not impossible.

Designing an engineering system like a programming language is often a matter of trade-offs. If in my code I find a problem (like integer overflows) quite more common than other ones (like bugs caused by implicit bool->int conversions) it is very right for me to desire the first ones issued first. Priorities are really important in engineering.
 Casts are powerful tools, they shut up the compiler and they assume the  
 programmer is perfectly right and has perfect knowledge of what's going  
 on.


 Do you really believe that the purpose of casts are to "shut up the  
 compiler"? Seriously?

I believe that casts often "shut up the compiler" but I don't belive that's their purpose. One of their main purposes is to offer a standard way to break the static type system in specific points of the program. Every type system restricts the number of the acceptable programs. But programmers sometimes want to write some of those programs. To do this they sometimes use casts. D casts have other secondary purposes, like bit reinterpretation, etc.
 In practice my experience shows that the programmer (me too) sometimes  
 doesn't have perfect knowledge (usually because the code later was  
 modified, turning the cast into a bug because casts are often silent).


 You realize that the exact argument can be made about implicit casts.

You are missing something important. Currently this code compiles, it performs a silent implicit cast: bool foo() { return true; } void main() { int x = foo(); } Now you change the code, foo returns a double, the implicit cast stops being accepted and the compiler gives an error: double foo() { return 1.5; } void main() { int x = foo(); } The same doesn't happen if you use an explicit cast. This is the original code if we now require a cast to assign a bool to an int: bool foo() { return true; } void main() { int x = cast(int)foo(); } Now if you modify the code, so foo returns a double, the cast keeps silencing the compiler and this is a possible bug that goes unnoticed (you lose information doing double->int, while bit->int doesn't lose information): double foo() { return 1.5; } void main() { int x = cast(int)foo(); } --------------------- Don:
 I think stuff like
 int z +=  x > y;
 should ideally require a cast. That's a crazy operation.

If D ints/bools change their semantics in that way, then I agree that BigInt should do the same. But until that moment... Bye, bearophile
Dec 24 2011
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/24/11 9:34 AM, Derek wrote:
 Yes, I agree that this is a potential source of bugs. So what we need is
 something more explicit.

 double foo() { return 1.5; }
 void main() {
 int x = cast(double:int)foo();
 }

 Now the code is very clear about your intentions for it, and if foo() is
 later modified to return some incompatible datatype, the compile can
 alert the coder.

auto explicitCast(From, To)(From value) { return cast(To) value; }
 And to be consistent, we need to have syntax that allows a coder to
 explicitly tell the compiler to do a re-interpretation cast.

auto reinterpretCast(From, To)(From value) if (...) { return cast(To) value; } No need for more syntax. Andrei
Dec 24 2011
prev sibling next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 12/24/2011 04:33 AM, Don wrote:
 On 24.12.2011 02:30, Derek wrote:
 On Sat, 24 Dec 2011 09:19:26 +1100, bearophile
 <bearophileHUGS lycos.com> wrote:

 This is why it's better to avoid casts, not requiring them in the
 first place, unless they are useful. In this case I think a cast
 introduces more danger than the risks caused by implicit bool->int
 conversions.

If we assume that explicit casts are required for bool->int conversion, can you show some code in which this could cause a problem?

I think stuff like int z += x > y; should ideally require a cast. That's a crazy operation.

Love that one... Andrei
Dec 24 2011
prev sibling parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 12/24/2011 11:33 AM, Don wrote:
 On 24.12.2011 02:30, Derek wrote:
 On Sat, 24 Dec 2011 09:19:26 +1100, bearophile
 <bearophileHUGS lycos.com> wrote:

 This is why it's better to avoid casts, not requiring them in the
 first place, unless they are useful. In this case I think a cast
 introduces more danger than the risks caused by implicit bool->int
 conversions.

If we assume that explicit casts are required for bool->int conversion, can you show some code in which this could cause a problem?

I think stuff like int z += x > y; should ideally require a cast.

What does anyone gain by adding bulky 'cast(int)' noise to their code that is nothing but a no-op?
 That's a crazy operation.

s/craz/nift/
 The problem is compatibility with ancient C code (pre-C99), where you
 may find:

 alias int BOOL;

 BOOL b = x > y;

 Although BOOL is typed as 'int', it really has the semantics of 'bool'.
 We have an example of this in D1's opEquals().
 I think this is reason why implicit conversion bool -> int exists.

It exists because it is handy and makes sense. Would you also want to ban implicit short -> int conversion?
 BTW, great to see you again, Derek!

Dec 24 2011
parent Timon Gehr <timon.gehr gmx.ch> writes:
On 12/24/2011 05:02 PM, Derek wrote:
 On Sun, 25 Dec 2011 02:50:15 +1100, Timon Gehr <timon.gehr gmx.ch> wrote:

 Although BOOL is typed as 'int', it really has the semantics of 'bool'.
 We have an example of this in D1's opEquals().
 I think this is reason why implicit conversion bool -> int exists.

It exists because it is handy and makes sense. Would you also want to ban implicit short -> int conversion?

A 'short' is a type of integer, an 'int' is a type of integer, but a 'bool' is NOT a type of integer. One can do arithmetic with two integers

If so, then 'short' is not a type of integer.
 but what does 'TRUTH * TRUTH'
 mean? Or what does 'FALSEHOOD - TRUTH' mean?

TRUTH and FALSEHOOD are in my understanding not values, so performing operations on them is nonsensical. Having the symbols {0, 1} as the boolean values is a common convention, even outside programming language implementations or computer science/digital design. So TRUE * TRUE = 1*1 = 1 and FALSE - TRUE = 0 - 1 = -1 makes sense.
Dec 24 2011
prev sibling next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, December 23, 2011 17:19:26 bearophile wrote:
 Derek Parnell:
 I'm with Don on this one because a boolean and an integer are not the
 same concept, and even though many programming languages implement
 booleans using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs.

I'd actually argue that that's a mistake. Implicitly converting an int to a bool is one thing - that's useful in conditional expressions - but converting from bool to int is something else entirely. I see no reason to expand that problem into BigInt. _int_ shouldn't have it, let alone BigInt. - Jonathan M Davis
Dec 23 2011
parent bearophile <bearophileHUGS lycos.com> writes:
Jonathan M Davis:

 I'd actually argue that that's a mistake.
 ...
 I see no reason to expand that 
 problem into BigInt. _int_ shouldn't have it, let alone BigInt.

I find implicit bool->int conversion handy, but... I like better the Pascal/Ada way of keeping ints and bools more distinct. I don't like this aspect of the C language that D has inherited. So do you want to write an enhancement request to change the way D ints and bools behave? OK. When D ints and bools will be changed the way you say then I'll be happy to see BigInts changed back to refuse assignments from bools. But now 99.9+% of the integral values you find in D programs are not bigInts, so you are "expanding" something tiny. You are not improving code, you are just making BigInts a bit weird compared to most other D code. Bye, bearophile
Dec 23 2011
prev sibling next sibling parent Derek <ddparnell bigpond.com> writes:
On Sat, 24 Dec 2011 09:19:26 +1100, bearophile <bearophileHUGS lycos.com>  
wrote:

 Derek Parnell:

 I'm with Don on this one because a boolean and an integer are not the  
 same
 concept, and even though many programming languages implement booleans
 using integers, it still doesn't make them the same thing.

D doesn't implement booleans with integers, D has a boolean type. But D allows bools to implicitly cast to ints/longs. Not allowing a BigInt to be initialized with a bool value introduce an inconsistency that makes BigInts more complex because there is one more rule to remember, less inter-operable with ints, and I don't think it introduces advantages.

I agree that 'consistency' is a powerful argument. So it comes down to is D meant to be the best language or an adequate language. I maintain that D would be a better language if it didn't allow implicit bool <-> int conversions. The most common thing that humans do to source code is read it, in order to understand it's purpose and/or intentions. We would do ourselves a service if we strive to make programing languages aid this activity. Some implicit conversions can mask a coder's intentions, and I believe that bool/int is one of those.
 Using booleans as implicit integers can be seen as laziness (i.e. poor
 documentation of coder's intent) or a legitimate mistake (i.e
 unintentional usage by coder).

In my code such mistakes are uncommon.

But not impossible.
 By insisting that an explicit cast must be
 used when one wants a boolean to behave as an integer allows the coder's
 intent to become more apparent when reading their source code. This has
 nothing to do with machine code generation, just source code legibility.

Casts are powerful tools, they shut up the compiler and they assume the programmer is perfectly right and has perfect knowledge of what's going on.

Do you really believe that the purpose of casts are to "shut up the compiler"? Seriously?
 In practice my experience shows that the programmer (me too) sometimes  
 doesn't have perfect knowledge (usually because the code later was  
 modified, turning the cast into a bug because casts are often silent).

You realize that the exact argument can be made about implicit casts.
 This is why it's better to avoid casts, not requiring them in the first  
 place, unless they are useful. In this case I think a cast introduces  
 more danger than the risks caused by implicit bool->int conversions.

If we assume that explicit casts are required for bool->int conversion, can you show some code in which this could cause a problem? -- Derek Parnell Melbourne, Australia
Dec 23 2011
prev sibling next sibling parent Derek <ddparnell bigpond.com> writes:
On Sat, 24 Dec 2011 21:33:33 +1100, Don <nospam nospam.com> wrote:

 I think stuff like
 int z +=  x > y;
 should ideally require a cast. That's a crazy operation.

I would go so fas as saying that such an idiom ought to be coded as ... if (x > y) { z++; } or if insists on a one-liner ... z += (x > y ? 1 : 0); -- Derek Parnell Melbourne, Australia
Dec 24 2011
prev sibling next sibling parent Derek <ddparnell bigpond.com> writes:
On Sun, 25 Dec 2011 00:09:08 +1100, bearophile <bearophileHUGS lycos.com>  
wrote:

 Derek Parnell:

 In my code such mistakes are uncommon.


 But not impossible.

Designing an engineering system like a programming language is often a matter of trade-offs. If in my code I find a problem (like integer overflows) quite more common than other ones (like bugs caused by implicit bool->int conversions) it is very right for me to desire the first ones issued first. Priorities are really important in engineering.

I agree that priorities are extremely important. However, I'm not seeing that this bool->int conversion is an either-or situation. We are not being forced to choose between integer overflow issues and bool->int conversion issues. Our code should, right now, cater for both issues as it is not really a great deal of work to write code that deals with both.
 I believe that casts often "shut up the compiler" but I don't belive  
 that's their purpose. One of their main purposes is to offer a standard  
 way to break the static type system in specific points of the program.  
 Every type system restricts the number of the acceptable programs. But  
 programmers sometimes want to write some of those programs. To do this  
 they sometimes use casts. D casts have other secondary purposes, like  
 bit reinterpretation, etc.

Yes, 'cast' is a poor choice of word here. Sometimes we need to tell the compiler to do a data conversion (transforming bits into a new format) and sometimes we need to tell it to assume that the bits are already in the correct format even though it would not otherwise agree with you. One issue you have highlighted below is that 'cast(T)' is not exactly totally explicit. The coder cannot simply tell if it is doing a data conversion or a data re-interpretation. That requires some background knowledge about D's internals. Furthermore, when doing a data conversion, the 'cast(T)' syntax is only explicit about the target format; it implies the source format from the datatype of its operand. And as you point out below, that can be a source of bugs.
 In practice my experience shows that the programmer (me too) sometimes
 doesn't have perfect knowledge (usually because the code later was
 modified, turning the cast into a bug because casts are often silent).


 You realize that the exact argument can be made about implicit casts.

You are missing something important. Currently this code compiles, it performs a silent implicit cast: bool foo() { return true; } void main() { int x = foo(); } Now you change the code, foo returns a double, the implicit cast stops being accepted and the compiler gives an error: double foo() { return 1.5; } void main() { int x = foo(); } The same doesn't happen if you use an explicit cast. This is the original code if we now require a cast to assign a bool to an int: bool foo() { return true; } void main() { int x = cast(int)foo(); } Now if you modify the code, so foo returns a double, the cast keeps silencing the compiler and this is a possible bug that goes unnoticed (you lose information doing double->int, while bit->int doesn't lose information): double foo() { return 1.5; } void main() { int x = cast(int)foo(); }

Yes, I agree that this is a potential source of bugs. So what we need is something more explicit. double foo() { return 1.5; } void main() { int x = cast(double:int)foo(); } Now the code is very clear about your intentions for it, and if foo() is later modified to return some incompatible datatype, the compile can alert the coder. And to be consistent, we need to have syntax that allows a coder to explicitly tell the compiler to do a re-interpretation cast. -- Derek Parnell Melbourne, Australia
Dec 24 2011
prev sibling next sibling parent Derek <ddparnell bigpond.com> writes:
On Sun, 25 Dec 2011 02:50:15 +1100, Timon Gehr <timon.gehr gmx.ch> wrote:

 Although BOOL is typed as 'int', it really has the semantics of 'bool'.
 We have an example of this in D1's opEquals().
 I think this is reason why implicit conversion bool -> int exists.

It exists because it is handy and makes sense. Would you also want to ban implicit short -> int conversion?

A 'short' is a type of integer, an 'int' is a type of integer, but a 'bool' is NOT a type of integer. One can do arithmetic with two integers but what does 'TRUTH * TRUTH' mean? Or what does 'FALSEHOOD - TRUTH' mean? -- Derek Parnell Melbourne, Australia
Dec 24 2011
prev sibling parent Derek <ddparnell bigpond.com> writes:
On Sat, 24 Dec 2011 21:33:33 +1100, Don <nospam nospam.com> wrote:

 I think stuff like
 int z +=  x > y;
 should ideally require a cast. That's a crazy operation.

Another issue is that it is a mere convention that C uses 1 to represent TRUE and 0 to represent FALSE. There are some languages that use 0 for FALSE (all bits off) and -1 for TRUE (all bits on). -- Derek Parnell Melbourne, Australia
Dec 24 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, December 23, 2011 23:52:00 Timon Gehr wrote:
 There is really no problem with that. I have never seen anyone complain
 about implicit bool -> int conversion. Why do you think it is bad? Does
 anyone have an example to back up the claim that it is bad?

They're completely different types and mean completely different things. It's one thing to convert from a narrower integer to a wider one, but bool is _not_ an integer. Would you implicitly convert a string to an int? No. It's not a number. I don't see any reason to treat bool any differently on that count. bool isn't a number either. It's true or it's false. The problem is that C conflated bool with int, and on some level that behavior still exists in D. But bool and int are two entirely different types and entirely different concepts. - Jonathan M Davis
Dec 23 2011
prev sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Saturday, December 24, 2011 01:08:11 Timon Gehr wrote:
 bool and int are in no way 'entirely different concepts'. Both are
 fields. bool is (Z_2, ^, &) , int is (Z_(2^32), +, *). string is
 conceptually a monoid.
 
 Boolean algebra is the algebra of two values. At least in computer
 science or digital design, those two values are 0 and 1. If there are
 implicit conversions in a language at all, implicit bool -> int is a
 natural thing to do. There is no such argument for string -> int.

Boolean has the values are true and false. The fact that it's implemented as 1 and 0 is an implementation detail. Conceptually, a bool is _not_ a number any more than a string is. As such, it shouldn't implicitly convert to a number any more than a string does. - Jonathan M Davis
Dec 23 2011